CF472G Increase the Constraints

Increase the Constraints

定义两个等长的01字符串的汉明距离为它们字符不同的对应位置的个数。

给你两个01串S,T,现在有q个询问,每次指定S,T中两个定长的子串询问它们的汉明距离。

1≤|S|,|T|≤200000,1≤q≤400000

cz_xuyixuan的题解

字符不同=长度-字符相同。考虑到两个字符串的匹配问题可以用FFT处理,于是往FFT方面考虑。

分块FFT,令分块大小为B,进行O(\(\frac{n}{B}\))次FFT,处理出O(\(\frac{n}{B}\))个T的后缀与S的每个后缀能够匹配的位数。询问时容斥一下并加上边角暴力就好了。

这样的时间复杂度是O(\(\frac{n^2\log n}{B}\)+qB),取B=n\(\sqrt{\frac{\log n}{q}}\)=‭1,327.013205时,可以获得渐进意义下最优复杂度O(n\(\sqrt{q log n}\))。

有一种高妙的做法来解决01匹配问题。我们令0为1,1为-1,然后FFT。那么两个字符如果匹配,得数为1,否则为-1。我们给1和-1的总和加上长度,那么就变成了匹配得2,不匹配得0.

由于NTT常数大,所以程序取B=7200。

CO int N=524288;
int rev[N],omg[N];

void NTT(int a[],int lim,int dir){
    for(int i=0;i<lim;++i)
        if(i<rev[i]) swap(a[i],a[rev[i]]);
    for(int i=1;i<lim;i<<=1)
        for(int j=0;j<lim;j+=i<<1)
            for(int k=0;k<i;++k){
                int t=mul(omg[lim/(i<<1)*k],a[j+i+k]);
                a[j+i+k]=add(a[j+k],mod-t),a[j+k]=add(a[j+k],t);
            }
    if(dir==-1){
        int ilim=fpow(lim,mod-2);
        for(int i=0;i<lim;++i) a[i]=mul(a[i],ilim);
    }
}

CO int B=7200;
char s[N],t[N];int ls,lt;
int index[N],l[N],r[N],tot;
int a[N],b[N],ans[N/B][N];

int query(int ps,int pt){
    int ans=0;
    if(ps+B>=ls or pt+B>=lt){
        for(;ps<ls and pt<lt;++ps,++pt) ans+=s[ps]==t[pt];
        return ans;
    }
    for(;index[pt]==index[pt-1];++ps,++pt) ans+=s[ps]==t[pt];
    ans+=::ans[index[pt]][ps];
    return ans;
}
int main(){
    scanf("%s%s",s,t);
    ls=strlen(s),lt=strlen(t);
    for(int i=0;i<lt;++i){
        if(i%B==0) l[++tot]=i;
        index[i]=tot,r[tot]=i;
    }
    for(int p=1;p<=tot;++p){
        memset(a,0,sizeof a);
        for(int i=0;i<ls;++i) a[i]=s[i]=='0'?1:mod-1;
        memset(b,0,sizeof b);
        for(int i=l[p];i<lt;++i) b[lt-1-i]=t[i]=='0'?1:mod-1;
        int n=lt-l[p]-1;
        
        int len=ceil(log2(ls+n)),lim=1<<len;
        for(int i=0;i<lim;++i) rev[i]=rev[i>>1]>>1|(i&1)<<(len-1);
        omg[0]=1,omg[1]=fpow(3,(mod-1)/lim);
        for(int i=2;i<lim;++i) omg[i]=mul(omg[i-1],omg[1]);
        NTT(a,lim,1),NTT(b,lim,1);
        for(int i=0;i<lim;++i) a[i]=mul(a[i],b[i]);
        omg[1]=fpow(omg[1],mod-2);
        for(int i=2;i<lim;++i) omg[i]=mul(omg[i-1],omg[1]);
        NTT(a,lim,-1);
        
        for(int i=0;i<ls;++i){
            ans[p][i]=add(a[i+n],add(n+1,mod-max(0,i+n-ls+1))); // edit 1
            ans[p][i]=mul(ans[p][i],i2);
        }
    }
    for(int q=read<int>();q--;){
        int ps=read<int>(),pt=read<int>(),n=read<int>();
        printf("%d\n",n-query(ps,pt)+query(ps+n,pt+n));
    }
    return 0;
}

处理ans数组的时候还是要放到模意义下,因为1和-1的总和可能为负数。

猜你喜欢

转载自www.cnblogs.com/autoint/p/12027371.html