1. 程式人生 > 實用技巧 >[PKUSC2018]神仙的遊戲 題解

[PKUSC2018]神仙的遊戲 題解

[PKUSC2018]神仙的遊戲 題解

Problem

​ 小D和小H發現了一種新的遊戲。

​ 給出一個由0/1/?組成的字串 \(s\) ,將 \(s\) 中的問號用0/1替換,對每個 \(l\) 口算是否存在替換問號的方案使得 \(s\) 長度為 \(l\) 的字首成為border,把這個結果記做 \(f(l)=0/1\)

請計算 \((f(1) \times 1^2) \otimes (f(2) \times 2^2) \otimes \cdots \otimes (f(n) \times n^2)\)

\(|s| \leq 5 \times 10^5\)

Solution

對於一個長度為 \(len\)

的border,必定會有 \(s_{i}=s_{n-len+i}\)

我們可以得到一個這樣的性質:若任取 \(i \equiv j (\mod x)\) ,都有 \(s_i=s_j\) ,則 \(n-x\) 是一個border。

所以,倘若 \(i \equiv j (\mod x)\)\(s_i \neq s_j\) ,則 \(n-x\) 必定不是一個border。即只要知道有一對01的差是 \(d\) ,就一定會有對於一個 \((n-len)|d\)\(len\) 都不是一個border。

所以我們只要求出兩兩01的差,就能判斷border,而這個只要FFT即可。

Code

#include<bits/stdc++.h>
#define DB double
#define LL long long
using namespace std;

const DB Pi=acos(-1);

LL n,Ans;
char s[500005];
int vis[500005],rev[2000005];

struct Complex{

    DB x,y;

    inline Complex operator + (const Complex &A)const{
        return (Complex){x+A.x,y+A.y};
    }

    inline Complex operator - (const Complex &A)const{
        return (Complex){x-A.x,y-A.y};
    }

    inline Complex operator * (const Complex &A)const{
        return (Complex){x*A.x-y*A.y,x*A.y+y*A.x};
    }

}F[2000005],G[2000005];

void FFT(Complex *F,int Lim,int op){
    for(register int i=0;i<Lim;++i){
        if(i<rev[i])
            swap(F[i],F[rev[i]]);
    }
    for(register int mid=1;mid<Lim;mid<<=1){
        int R=mid<<1;
        Complex rt=(Complex){cos(Pi/mid),op*sin(Pi/mid)};
        for(register int j=0;j<Lim;j+=R){
            Complex w=(Complex){1,0};
            for(register int k=0;k<mid;++k){
                Complex x=F[j|k],y=w*F[j|k|mid];
                F[j|k|mid]=x-y;
                F[j|k]=x+y;
                w=w*rt;
            }
        }
    }
    if(op==-1){
        for(register int i=0;i<Lim;++i){
            F[i].x/=Lim;
            F[i].y/=Lim;
        }
    }
    return;
}

int main(){
    
    scanf("%s",s+1);n=strlen(s+1);

    int Lim=1,Len=-1;
    while(Lim<(n+1<<1))
        Lim<<=1,++Len;
    for(register int i=0;i<Lim;++i)
        rev[i]=(rev[i>>1]>>1)|((i&1)<<Len);

    for(register int i=0;i<n;++i)
        F[i].x=(s[i+1]=='0');

    for(register int i=0;i<n;++i)
        G[i].x=(s[n-i]=='1');

    FFT(F,Lim,+1);FFT(G,Lim,+1);
    for(register int i=0;i<Lim;++i)
        F[i]=F[i]*G[i];
    FFT(F,Lim,-1);

    for(register int i=1;i<(n<<1);++i)
        vis[abs(i-n+1)]+=(F[i].x+0.5);

    for(register int i=1;i<n;++i){
        int flag=1;
        for(register int j=1;i*j<=n;++j){
            if(vis[i*j]){
                flag=0;
                break;
            }
        }
        if(flag)
            Ans^=1LL*(n-i)*(n-i);
    }

    printf("%lld\n",Ans^(1LL*n*n));

    return 0;
}