「PKUSC2018」神仙的游戏 [FFT]

2 篇文章 0 订阅
1 篇文章 0 订阅

「PKUSC2018」神仙的游戏

Tags: FFT KMP Border


「PKUSC2018」神仙的游戏

题意

对于一个长度为n的字符串。

fi={10if(s[1...i]=s[ni+1...n])if(s[1...i]s[ni+1...n]) f i = { 1 i f ( s [ 1... i ] = s [ n − i + 1... n ] ) 0 i f ( s [ 1... i ] ≠ s [ n − i + 1... n ] )

要求 (f1i2)xor(f222)...xor(fnn2) ( f 1 ∗ i 2 ) x o r ( f 2 ∗ 2 2 ) . . . x o r ( f n ∗ n 2 )

分析

border
很好这个(对我来说)新奇的东西把我这种什么都不会的苣蒻淘汰下去了。
这个东西来自KMP算法。暂且不考虑和KMP算法的联系,单单来看border的性质有什么呢。
以下内容全部来自这篇题解

定理内容:
s[1,i]=s[ni+1,n] s [ 1 , i ] = s [ n − i + 1 , n ] 其实就是对于s有长度为(n-i)的循环节 (最后一个循环节不需要完整)

推论1
对于任意的i满足 si=sni+1 s i = s n − i + 1
推论2
如果s不存在长度为d的循环节,那么长度为d的倍数的循环节也一定不存在
推论3
如果对于所有的d,所有长度为d的因数的循环节都存在,那么一定存在长度为d的循环节。

然后根据推论2和推论3就可以完成这道题了。
首先找出所有的01对,记录两者的距离d,那么就不存在长度为d的倍数的循环节。
然后首先找出所有的d,然后再用类似埃氏筛的方法就可以把所有的循环节找出来了。

但是对于暴力找所有的01对,复杂度是 n2 n 2 的,这里可以通过fft或者ntt来优化一下,快速求出所有距离是否存在01对。

a[i]={1,0,if s[i]=0 if s[i] 0b[ni]={1,0,if s[i]=1if s[i] 1 a [ i ] = { 1 , if s[i]=0  0 , if s[i] ≠  0 b [ n − i ] = { 1 , if s[i]=1 0 , if s[i] ≠  1

卷积后的结果记作c,是否存在长度为d的border记作f[d]那么有
f[n]={1,0,if n|if[i]=0 and c[n-i]=0 and c[n+i]=0other f [ n ] = { 1 , if  ∑ n | i f [ i ] = 0  and c[n-i]=0 and c[n+i]=0 0 , other


以下是AC了之后的扯淡。
知道这个东西的性质之后其实还是蛮水的,但是不知道的话当场推好像还是稍微有点难度啊qwq?
然后就是写了最后一挡之后的确很好用fft优化,但是也很容易因为各种原因挂掉诶?(挂了一次)

code
#include<bits/stdc++.h>
#define M 2000005 
#define db double
#define ll long long
using namespace std;
struct Complex{
    db r,i;
    Complex(db R=0,db I=0){
        r=R; i=I;
    }
};
Complex operator +(const Complex &x,const Complex &y){
    return Complex(x.r+y.r,x.i+y.i);
}
Complex operator -(const Complex &x,const Complex &y){
    return Complex(x.r-y.r,x.i-y.i);
}
Complex operator *(const Complex &x,const Complex &y){
    return Complex(x.r*y.r-x.i*y.i,x.r*y.i+x.i*y.r);
}
Complex operator / (const Complex &x,const int &y){
    return Complex(x.r/y,x.i/y);
}

int flag[M],n;
char s[M];
const db pi=acos(-1.0);
struct FFT{
    Complex A[M],B[M];
    int rev[M];
    void solve(Complex *a,int n,int DFT){
        register int i,j,k,m;
        Complex l,r,w,wn;
        for (i=0;i<n;i++)if (i<rev[i])swap(a[i],a[rev[i]]);
        for (m=1;m<n;m<<=1){
            k=(m<<1);
            w=Complex(1,0);
            wn=Complex(cos(pi*DFT/m),sin(pi*DFT/m));
            for (i=0;i<m;i++){
                for (j=i;j<n;j+=k){
                    l=a[j]; r=a[j+m];
                    a[j]=l+w*r;
                    a[j+m]=l-w*r;
                }
                w=w*wn;
            }
        }
        if (DFT==-1){
            for (i=0;i<n;i++)a[i]=a[i]/n;
        }
    }
    void solve(int *a,int *b,int n){
        int i,k;    
        for (k=1;k<n;k<<=1);
        for (i=0;i<k;i++){
            A[i]=Complex(a[i],0);
            B[i]=Complex(b[i],0);
            rev[i]=(rev[i>>1]>>1)|((i&1)*(k>>1));
        }
        solve(A,k,1); solve(B,k,1);
        for (i=0;i<k;i++)A[i]=A[i]*B[i];
        solve(A,k,-1);
        for (i=0;i<k;i++){
            a[i]=(int)(A[i].r+0.5);
        }
    }   
}fft;
int a[M],b[M];
int main(){
//  freopen("1.in","r",stdin);
    scanf("%s",s+1);
    n=strlen(s+1);
    int i,j;
    ll res=0;
    for (i=1;i<=n;i++){
        if (s[i]=='1')a[i]=1;
        if (s[i]=='0')b[n-i]=1;
    }
    fft.solve(a,b,2*n+5);
    for (i=0;i<n;i++){
        flag[i]=(a[n+i]|a[n-i])>0;
    }
    for (i=1;i<=n;i++)if (!flag[i]){
        for (j=i+i;j<=n;j+=i){
            flag[i]|=flag[j];
        }
    }
    for (i=1;i<=n;i++){
        res^=1ll*i*i*(1-flag[n-i]);
    }
    printf("%lld\n",res);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值