联赛%你测试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)查询总没问题吧
- 评测机没开c++11,根本用不了。
- unordered_map实测也会T。
- 所以这道题想通过这种方法a掉,就只剩两种方法了:
- 运用神仙快读fread()+强行预编译c++11
- 手打哈希表。
就没别的办法了吗???
当然有。
我们考虑差分(区间问题的一种可行性方法),先建出来差分数组。
每次修改是对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;
}