題目鏈接:點擊查看
題目大意:在一個公司中初始時有 n 個人,每個人都有一個能力值,題目保證每個能力值都不相等,第 i 天會開除能力值最低的 r[ i ] 個人,并且會新加入 r[ i ] 個人,分別是 b[ i ][ 1 ] ~ b[ i ][ r[ i ] ],現在有 q 次詢問,每次詢問會給出三個參數:x , y , z ,意思就是將 b[ x ][ y ] 改成 z 后,第一個人最后會不會開除,會被開除的話輸出 0 ,否則輸出 1
題目分析:看起來是需要維護一個排過序的關系,但實際上我們只需要維護一下相對的大小關系即可,因為題目中保證了能力值互不相同,所以我們可以將除了第一個人以外的人分為兩種人:(下面我都會將第一個人稱之為目標)
能力值大于目標的人 能力值小于目標的人
兩種方向都可以解決這個問題,這里我選擇的是維護能力值小于目標的人
已知第 i 天會開除的人:r[ i ] 個能力值最低的人,又可以通過新加入的 r[ i ] 個人求出第 i 天新加入的人中有多少個人小于目標,記為 lower[ i ] ,最開始還沒有操作的時候,也就是初始時 n 個人中小于目標的人數記為 lower[ 0 ] 即可
這樣一來,對于每一天來說,我們只需要保證對于每一天來說?( 能力值小于目標的人數 ) >= ( 被開除的人數 ) 全部成立,移個項就是 ( 能力值小于目標的人數 ) - ( 被開除的人數 ) >= 0 ,不難發現被開除的人數是一個定值,也就是 r[ i ] ,而能力值小于目標的人數,這個可以通過前一項迭代計算出,我們給表達式賦予意義,那么 ( 能力值小于目標的人數 ) - ( 被開除的人數 ) 的意義就是開除員工后,公司內剩下的人,再加上一項變成?( 能力值小于目標的人數 ) - ( 被開除的人數 ) + ( 新增加人數中小于目標的人數 ),此時這個表達式的意義就變為了當前公司內有多少個能力值小于目標的人數(能力值大于目標的人數顯然不起作用,忽略即可),這樣我們可以求出最開始需要求的迭代式:
ans[ i ] = ans[ i - 1 ] + lower[ i - 1 ]? - r[ i ]?
那么對于某次修改會造成什么影響呢,考慮將 b[ x ][ y ] 變為 z ,那么只會讓第 x 位上的 lower[ x ] 加一或者減一,不難發現需要維護的 ans 數組是一個前綴和,對 ans 數組造成的影響就是 [ x + 1 ~ n ] 個位置都需要加一或者減一,這樣就可以將前綴和放到線段樹上維護了,維護一下區間最小值用來判斷就可以了
代碼: ?
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e5+100;int lower[N],a[N];vector<int>b[N];struct Node
{int l,r;int mmin;int lazy;
}tree[N<<2];void pushup(int k)
{tree[k].mmin=min(tree[k<<1].mmin,tree[k<<1|1].mmin);
}void pushdown(int k)
{if(tree[k].lazy){int lz=tree[k].lazy;tree[k].lazy=0;tree[k<<1].mmin+=lz;tree[k<<1|1].mmin+=lz;tree[k<<1].lazy+=lz;tree[k<<1|1].lazy+=lz;}
}void build(int k,int l,int r)
{tree[k].l=l;tree[k].r=r;tree[k].lazy=0;if(l==r){tree[k].mmin=lower[l];return;}int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k);
}void update(int k,int l,int r,int val)
{if(l>r)return;if(tree[k].r<l||tree[k].l>r)return;if(tree[k].l>=l&&tree[k].r<=r){tree[k].mmin+=val;tree[k].lazy+=val;return;}pushdown(k);update(k<<1,l,r,val);update(k<<1|1,l,r,val);pushup(k);
}int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);int n,m,q;scanf("%d%d%d",&n,&m,&q);for(int i=1;i<=n;i++){scanf("%d",a+i);if(a[i]<a[1])lower[0]++;}int pre=0;//上一次加個多少個比a[1]小的數有多少 for(int i=1;i<=m;i++){int num;scanf("%d",&num);lower[i]=lower[i-1]+pre-num;pre=0;b[i].push_back(-1);while(num--){int x;scanf("%d",&x);b[i].push_back(x);if(x<a[1])pre++;}}build(1,1,m);while(q--)//lower的公式:lower[i]=lower[i-1]+pre-num,num是不變的,變的是pre {int x,y,z;scanf("%d%d%d",&x,&y,&z);if(b[x][y]>a[1]&&a[1]>z)//改變之后第x位置的pre->pre+1 update(1,x+1,m,1);if(b[x][y]<a[1]&&a[1]<z)//改變之后第x位置的pre->pre-1 update(1,x+1,m,-1);b[x][y]=z;if(tree[1].mmin<0)puts("0");elseputs("1");}return 0;
}
?
總結
以上是生活随笔 為你收集整理的CodeForces - 1252G Performance Review(线段树+思维) 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。