联赛%你测试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;
}
posted @ 2020-10-07 11:40  刘益通  阅读(387)  评论(8编辑  收藏  举报