#6500. 「雅礼集训 2018 Day2」操作 题解

更好的阅读体验

#6500. 「雅礼集训 2018 Day2」操作

题目描述

有一个长度为 \(n\) 的 序列,\(m\) 次询问,每次询问给出一个区间,你可以进行若干次操作,每次选择这个区间的一个长度为\(k\) 的子区间,并将这个子区间的所有\(01\) 取反,求至少需要几次操作才能将这个区间内的所有元素变成 \(0\)

请注意,每次询问都是独立的,你在一个询问中进行的操作不会影响另一个询问。

输入格式

第一行包括三个正整数 。\(n,k,m\)

第二行给出一个长度为 \(n\)\(01\) 串,表示这个序列。

接下来 \(m\) 行,每行两个正整数,表示询问的区间。

输出格式

对每个询问输出一个整数表示答案,如果不能将区间内所有元素都变为 \(0\),输出 \(-1\)

\(n\leq2*10^6\)

\(m\leq 5*10^5\)


题解部分:

首先区间取反,让我想起了一道之前做过的题,于是果断将整个序列进行异或差分。

于是原本将区间 \([l,l+k-1]\) 取反就变成了将 \(l,l+k\) 这两个点取反。

接下来的思路皆是基于差分:

先来讲讲我的考场思路吧:

因为全是询问,又没有强制在线,于是我想到了我的弱鸡莫队(逃),

考虑这个区间取反操作:根据上面的差分思路,我们不难想到能同时消去两个 \(1\) ,当且仅当这两个位置 \(mod\) \(k\) 的余数相等。(不同区间对于这个是没有影响的)

每次查询的时候判断每一类中的 \(1\) 是否为偶数,如果不是,则无解。再贪心地想,消去时一定是两个相邻的消去,于是将同一类中的 \(1\) 消去所用的次数就是两两配对的位置差 \(/k\) 的和。

于是用莫队维护每类中 \(1\) 的个数与两两配对的位置差就可以了,左右端点带来的改变特殊处理。

时间复杂度 \(O(n\sqrt{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('\n');
        }
    }
    return 0;
}

posted @ 2021-04-12 16:40  NuoCarter  阅读(806)  评论(0编辑  收藏  举报