1. 程式人生 > 實用技巧 >聯賽%你測試10T2:漫無止境的八月

聯賽%你測試10T2:漫無止境的八月

題意:

思路:
有幾個特殊的性質:

  • 在不考慮q裡面的單點修改,我們先只判斷一個序列是否Yes。
    • 我們注意到每次操作都是對一個長度為k的區間進行區間加減1的操作,所以我們如果將序列裡面的數按%k分組,把同一組的數都加在一起,那每次操作就一定是給每一個組都加上或減去一個1,因為連續的k長度中,一定是每一組都有且只有一個數受到處理。
    • 因為我們自己的操作是區間加減,那麼操作是可逆的,如果這個序列能變成全0的序列,那麼他也一定可以由全0的序列轉移過來,全0的序列每一組的和都是0,進行幾次操作後,只會給每一組都加上或減去一些1。所以如果一個序列可以由全0序列轉移來,那麼它每一組數的和都一定相等。
  • 加上單點修改後,會有什麼改變呢?
    • 很顯然,會改變某一組的和。
    • 如果改變後所有組的和都相等,那麼輸出Yes,否則輸出No
    • 這樣\(O(n)\)查詢顯然不行,那我們稍微優化下。
    • 我們開個桶,記錄某一個值出現了幾次,如果一個組的和是x,那麼vis[x]++;如果一個值的vis是k,那麼就說明所有組的和都一樣,那麼就輸出yes啦。
    • 在修改的時候,我們把修改前的這組和的vis值--,修改後的這組和vis值++,然後判斷就好啦。
      然後就完了嗎???
      當然不,區間和的值太大了,陣列開不下。
      有人會說:用map呀,map開的下。
      map常數巨大,直接T飛(70分)。
  • 有人會說:用unordered_map呀,O(1)查詢總沒問題吧
    1. 評測機沒開c++11,根本用不了。
    2. unordered_map實測也會T。
  • 所以這道題想通過這種方法a掉,就只剩兩種方法了:
    1. 運用神仙快讀fread()+強行預編譯c++11
    2. 手打雜湊表。

就沒別的辦法了嗎???

當然有。
我們考慮差分(區間問題的一種可行性方法),先建出來差分陣列。
每次修改是對i加一個值對i+k減去同一個值。
那我們再按%k分組求和,發現無論如何更改,每一組的和都是不變的(因為i和i+k在一組裡)。
因為全0序列的差分陣列每一組的和都是0,那麼只要一個序列每一組和都是0,那就Yes。
一個特判:
我們發現原序列的最後一段區間n-k+1~n,通過差分陣列的處理,是給n-k+1的位置上加一個值,再給n+1的位置上減一個值。但是n+1位置上是沒有數的。所以我們得出一個驚人的事實:
n+1所在那個組的和是隨便選的!!!
因為你修改這個組的數的時候,因為n+1這個位置上沒有數,那就是隨便選,那當然無論怎樣這組的和都可以看作0。

我自己的口胡證明:
n+1這一組之所以特殊,是因為這一組的最後一項chafen[n-k+1]的修改比較特殊,我們在處理原序列的最後一段n-k+1~n這一段時候,對應到差分陣列上是隻給chafen[n-k+1]進行加減的,而其他的區間都是前面加上一個,後面再減回去一個。所以我們可以認為,無論原序列如何,可以做到只更改chafen[n-k+1]而不對其他的差分陣列的值產生影響,那麼無論這一組差分陣列的和是多少,我們可以通過人為調控chafen[n-k+1]的值使得這一組的和最後變為0。因此,這一組的和是任取的。

所以詢問一個區間是否Yes,只要看它差分後的所有組(除了n+1那組)是不是和都是0即可。
我的做法deepinc&skyh學長的處理方法是:

  • 先掃一遍給的序列,算出差分陣列。記錄每組和,如果有某組和不是0,那麼cnt++。如果掃完後cnt==0就Yes否則就No。(需特判n+1%k這組)
  • 每次單點修改,就是對pos所在的組加上一個數,對pos+1所在的組減去一個數(差分陣列的性質)
  • 如果有一個組的和由非0變成了0那麼cnt--,如果一個組的和由0變成了非0,那麼cnt++。每一個修改過後,直接看cnt==0就Yes。(注意:如果pos或pos+1所在的組有在n+1%k那組的,那這一組就不用處理上面的這些了)

附上醜碼:

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e6+10;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
int a[maxn],chafen[maxn],num[maxn];
int n,k,q;
int main(){
	freopen("august.in","r",stdin);
	freopen("august.out","w",stdout);
	n=read();k=read();q=read();
	for(register int i=1;i<=n;i++){
		a[i]=read();
	}
	for(register int i=1;i<=n;i++){
		chafen[i]=a[i]-a[i-1];
	}
	for(register int i=1;i<=n;i++){
		num[i%k]+=chafen[i];
	}
	int ignore=(n+1)%k;
	int cnt=0;
	for(register int i=0;i<k;i++){
		if(num[i]){
			cnt++;
		}
	}
	if(num[ignore])cnt--;
	if(!cnt)puts("Yes");else puts("No");
	for(register int i=1;i<=q;i++){
		int pos,x;
		pos=read();
		x=read();
		if(pos%k!=ignore){
			if(num[pos%k]==0&&num[pos%k]+x!=0)cnt++;
			if(num[pos%k]!=0&&num[pos%k]+x==0)cnt--;
			num[pos%k]+=x;
		}
		if((pos+1)%k!=ignore){
			if(num[(pos+1)%k]==0&&num[(pos+1)%k]-x!=0)cnt++;
			if(num[(pos+1)%k]!=0&&num[(pos+1)%k]-x==0)cnt--;
			num[(pos+1)%k]-=x;
		}
		if(!cnt)puts("Yes");else puts("No");
	}
	return 0;
}