#6500. 「雅礼集训 2018 Day2」操作
题目描述
有一个长度为 (n) 的 序列,(m) 次询问,每次询问给出一个区间,你可以进行若干次操作,每次选择这个区间的一个长度为(k) 的子区间,并将这个子区间的所有(01) 取反,求至少需要几次操作才能将这个区间内的所有元素变成 (0)。
请注意,每次询问都是独立的,你在一个询问中进行的操作不会影响另一个询问。
输入格式
第一行包括三个正整数 。(n,k,m)。
第二行给出一个长度为 (n) 的 (01) 串,表示这个序列。
接下来 (m) 行,每行两个正整数,表示询问的区间。
输出格式
对每个询问输出一个整数表示答案,如果不能将区间内所有元素都变为 (0),输出 (-1) 。
(nleq2*10^6)
(mleq 5*10^5)
题解部分:
首先区间取反,让我想起了一道之前做过的题,于是果断将整个序列进行异或差分。
于是原本将区间 ([l,l+k-1]) 取反就变成了将 (l,l+k) 这两个点取反。
接下来的思路皆是基于差分:
先来讲讲我的考场思路吧:
因为全是询问,又没有强制在线,于是我想到了我的弱鸡莫队(逃),
考虑这个区间取反操作:根据上面的差分思路,我们不难想到能同时消去两个 (1) ,当且仅当这两个位置 (mod) (k) 的余数相等。(不同区间对于这个是没有影响的)
每次查询的时候判断每一类中的 (1) 是否为偶数,如果不是,则无解。再贪心地想,消去时一定是两个相邻的消去,于是将同一类中的 (1) 消去所用的次数就是两两配对的位置差 (/k) 的和。
于是用莫队维护每类中 (1) 的个数与两两配对的位置差就可以了,左右端点带来的改变特殊处理。
时间复杂度 (O(nsqrt{m})) qaq
正解:
哈希+前缀和
区间在 (mod) (k) 下的 (1) 的个数其实可以用哈希 (O(1)) 判断。
而同一类中的 (1) 的位置差可以看作 (1) 的位置正负交替地加起来。
如果询问区间的 (1) 的个数为偶数,直接前缀和相减就可以得出答案了。
但是因为差分,区间开头为 (0) 的话也有可能为 (1) ,反正区间端点特殊处理一下就可以了qwq。
(其实哈希只是用来判断区间是否每一类都是偶数个1)
哈希的过程:
将 (mod) (k) 的余数进行哈希,当在位置 (i) 有一个 (1) 时,将前缀哈希 (xor) 上这个位置 (i) (mod) (k) 对应的哈希值。
哈希余数其实简单就是 (rand) 一下就行了QWQ
而要取出区间的哈希值时,因为左右端点可能会因为一些原因原本为 (1) ,单差分后为 (0),所以还要 (xor) 上两个端点的影响
取出区间哈希值:
(pre_i) 表示前缀哈希
(hs_i) 表示这个余数对应的哈希值,
(a_i) 表示这个位置原本是否为 (1)
pre[L]^pre[R]^(a[L]*hs[L%k])^(a[R]*hs[(R+1)%k]);
Code:
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
template <typename T>
inline void read(T &x){
x=0;char ch=getchar();bool f=false;
while(!isdigit(ch)) f|=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
x=f?-x:x;
return;
}
template <typename T>
inline void print(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10^48);
return;
}
const int N=2e6+3,M=2e5+3;
int n,k,m,l[N],r[N],dis[N],pos[N];
ull hs[N],a[N],pre[N];
char s[N];
int main(){srand(time(0));
read(n),read(k),read(m);
scanf("%s",s+1);
for(register int i=0;i<k;++i) hs[i]=rand()*rand();
for(register int i=1;i<=n;++i){
a[i]=s[i]-'0';pre[i]=pre[i-1],dis[i]=dis[i-1];
if(a[i]^a[i-1]){
pre[i]^=hs[i%k];
dis[i]+=-(pos[i%k]<<1)+i;
pos[i%k]=i-pos[i%k];
}
l[i]=pos[i%k];
r[i]=pos[(i+1)%k];
}
int L,R;
while(m--){
read(L),read(R);
ull tep=pre[L]^pre[R]^(a[L]*hs[L%k])^(a[R]*hs[(R+1)%k]);
if(tep!=0) puts("-1");
else{
int res=dis[R]-dis[L];
if(a[L]==1) res-=L-(l[L]<<1);
if(a[R]==1) res+=R+1-(r[R]<<1);
print(res/k),putchar('
');
}
}
return 0;
}