zoukankan      html  css  js  c++  java
  • #6500. 「雅礼集训 2018 Day2」操作 题解

    更好的阅读体验

    #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;
    }
    
    
  • 相关阅读:
    Codeforces 1132D
    Codeforces 670F
    Codeforces 670E
    Codeforces 670E
    Codeforces 670E
    Codeforces 670
    Codeforces 1138
    Codeforces 1114E
    力扣21.合并两个有序链表
    力扣538.把二叉树转换为累加树
  • 原文地址:https://www.cnblogs.com/NuoCarter/p/14648730.html
Copyright © 2011-2022 走看看