Loading

「POI2011 R1」棒棒糖 Lollipop

「POI2011 R1」棒棒糖 Lollipop

给一个长度为\(n\)的由1和2构成的序列,\(m\)次询问,每次询问有没有一个子串的和为\(k\)

\(1\leq n,m\leq10^6,1\leq k\leq2\times10^6\).

一道很好的思维题,可惜被我乱搞过掉了。。。

首先可以将原序列转化,将所有的2展开成2个1。

此时题意转化为:给你一个序列,有一些位置不能作为开头,有一些位置不能作为结尾,问你是否能选出一个长度为\(k\)的子串。

我们深入分析,发现这个新序列有着以下性质:

  • 对于任意一个不能作为结尾的位置,它的后一位一定可以作为结尾。
  • 对于任意一个不能作为开头的位置,它的前一位一定不能作为结尾。
  • 序列最后一个位置一定可以作为结尾。

总结以上两条性质可以得出:

  • 对于任意两个不能/可以(状态相同)作为结尾的位置,他们一定对应了一个合法的子串。
  • 对于一个询问\(k\),如果无解,那么每一对距离为\(k\)的位置一定恰好一个可以作为结尾,一个不能作为结尾。

那么我们可以对整个序列进行染色,黑色表示这个位置可以作为结尾,白色表示这个位置不能作为结尾。

然后我们发现,整个序列一定会被划分成长度为\(k\)(除最后一段)的若干块,相邻两块中同一位置的所有颜色必定相反。

那么我们可以预处理出这个新序列的哈希值和将颜色反转之后的哈希值,这样就可以快速判定相邻两块中是否存在合法的子串。

至于如何定位这个子串的左端点,只需要在块内二分即可。

同时注意到对于每一个k我们都可以只做一次,这样的话时间复杂度为\(\Omicron(n\times(\frac{1}{1}+\frac{1}{2}+…+\frac{1}{n}))=\Omicron(n\log n)\),而定位每个串的端点时间复杂度为\(\Omicron(m\log n)\),所以总时间复杂度为\(\Omicron((n+m)\log n)\),可以通过本题。

注意:这道题在loj上时限非常小,必须使用单哈希才能通过。

#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
const int TT=10007;
const int maxn=2000005;
int n,nn,a[maxn],m,ans[maxn],ql[maxn],qr[maxn],pw[maxn],idx[maxn];
int hsh[2][maxn];
inline int read(){
    int res=0,f_f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f_f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=(res<<3)+(res<<1)+(ch-'0'),ch=getchar();
    return res*f_f;
}
inline int get_hsh(int d,int l,int r){
    if(!l) return hsh[d][r];
    return (hsh[d][r]-(1ll*hsh[d][l-1]*pw[r-l+1]%mod)+mod)%mod;
}
int main(){
    nn=read(),m=read(),pw[0]=1,a[n=0]=1;
    for (int i=1;i<=nn;i++){
        char ch=getchar();
        int x;
        while(ch!='W'&&ch!='T') ch=getchar();
        if(ch=='W') x=1;
        if(ch=='T') x=2;
        if(x==1) a[++n]=1,idx[n]=i;
        else a[++n]=2,idx[n]=i,a[++n]=1,idx[n]=i;
    }
    for (int i=1;i<=n;i++) pw[i]=(1ll*pw[i-1]*TT)%mod;
    hsh[0][0]=1,hsh[0][1]=2;
    for (int i=1;i<=n;i++){
        hsh[0][i]=(1ll*hsh[0][i-1]*TT%mod+a[i])%mod;
        hsh[1][i]=(1ll*hsh[1][i-1]*TT%mod+(3-a[i]))%mod;
    }
    for (int i=1;i<=m;i++){
        int x=read();
        if(ans[x]==0){
            bool flag=false;
            for (int d=1,l=x,r=l+x-1;l<=n;l+=x,r+=x,d^=1){
                r=min(r,n);
                int len=r-l+1;
                if(get_hsh(0,0,len-1)!=get_hsh(d,l,r)){
                    flag=true;
                    int L=l,R=r,mid,res=r;
                    while(L<=R){
                        mid=L+R>>1,len=mid-l+1;
                        if(get_hsh(0,0,len-1)!=get_hsh(d,l,mid)) res=mid,R=mid-1;else L=mid+1;
                    }
                    qr[x]=idx[res],ql[x]=idx[res-x+1];
                    break;
                }
            }
            if(flag) ans[x]=1;
            else ans[x]=-1;
        }
        if(ans[x]==1) printf("%d %d\n",ql[x],qr[x]);
        else printf("NIE\n");
    }
    return 0;
}

hoho!正解挖坑!

posted @ 2021-03-07 14:53  SmilingKnight  阅读(158)  评论(0编辑  收藏  举报