bzoj5372: [Pkusc2018]神仙的游戏 FFT

bzoj5372: [Pkusc2018]神仙的游戏

Description

小D和小H是两位神仙。他们经常在一起玩神仙才会玩的一些游戏,比如“口算一个4位数是不是完全平方数”。
今天他们发现了一种新的游戏:首先称s长度为len的前缀成为border当且仅当
s[1…len]=s[|s|-len+1…|s|]。
给出一个由01?组成的字符串s,将s中的问号用变成01替换,对每个len口算是否存在替换问号的方案使得s长度为len的前缀成为border,
把这个结果记做f(len)∈{0,1}。f(len)=1如果s长度为len的前缀能够成为border,否则f(len)=0
由于小D和小H是神仙,所以他们计算的s的长度很长,因此把计算的结果一一比对会花费很长的时间。为了方便比对,他们规定了一个校验值:
(f(1)*12)xor(f(2)*22)xor(f(3)*32)xor…xor(f(n)*n2)
来校验他们的答案是否相同。xor表示按位异或。
但是不巧,在某一次游戏中,他们口算出的校验值并不一样,他们希望你帮助他们来计算一个正确的校验值。
当然,他们不强迫你口算,可以编程解决。

Input

一个串s,保证每个字符都是0,1,或者?
∣s∣<=5*10^5

Output

输出字符串的校验值,即(f(1)*12)xor(f(2)*22)xor(f(3)*32)xor…xor(f(n)*n2)

Sample Input

1?0?

Sample Output

17
将问号填充为1001,则这个串有长度为1的border,故f(1)=1
将问号填充为1010,则这个串有长度为4的border,故f(4)=1
对于f(2)和f(3),可以枚举填充的字符是什么来证明他们的值是0。
故答案是1^2 xor 4^2=17

分析

这道神仙题终于是补回来了。
考试的时候觉得这道题和残缺的字符串很像,于是敲了一波FFT。
FFT倒是敲出来了,可是发现?可能同时对应0和1,然后心态崩了。
这道题要从0,1入手。
假设 b r o d e r = d broder=d broder=d,实际上就是把原串右移 n − d n-d nd,然后这个串和原来的串相等。
若存在一个长度为 x x x的循环节,那么就存在 b r o d e r = n − x broder=n-x broder=nx。那么判断循环节即可。
对于一对 i , j i,j i,j s [ i ] ≠ s [ j ] ≠ ? s[i]\ne s[j]\ne? s[i]̸=s[j]̸=?那么必定不存在长度为 ∣ i − j ∣ |i-j| ij的循环节,也不存在长度为 ∣ i − j ∣ |i-j| ij约数的循环节。
这个问题显然是一个经典的 F F T FFT FFT问题。令 A n − i = [ s i = 1 ] , B i = [ s i = 0 ] A_{n-i}=[s_i=1],B_i=[s_i=0] Ani=[si=1],Bi=[si=0],两个卷积的结果判一下就好了。

代码

#include<bits/stdc++.h>
const int N = 2e6 + 10;
const double pi = acos(-1.0);
int ri() {
    char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
    for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
struct cp {
    double r, i;
    cp(double _r = 0, double _i = 0) : r(_r), i(_i) {}
    cp operator + (cp a) {return cp(r + a.r, i + a.i);}
    cp operator - (cp a) {return cp(r - a.r, i - a.i);}
    cp operator * (cp a) {return cp(r * a.r - i * a.i, r * a.i + i * a.r);}
}w[N], A[N], B[N];
int R[N], a[N], b[N], L; bool d[N]; char s[N];
cp conj(cp a) {return cp(a.r, -a.i);}
void Pre(int m) {
    int x = 0; L = 1;
    for(;(L <<= 1) <= m; ++x) ;
    for(int i = 1;i < L; ++i) R[i] = R[i >> 1] >> 1 | ((i & 1) << x);
    for(int i = 0;i < L; ++i) w[i] = cp(cos(2 * pi * i / L), sin(2 * pi * i / L));   
}
void FFT(cp *F) {
    for(int i = 0;i < L; ++i) if(i < R[i]) std::swap(F[i], F[R[i]]);
    for(int i = 1, d = L >> 1;i < L;i <<= 1, d >>= 1)
        for(int j = 0;j < L; j += (i << 1)) {
            cp *l = F + j, *r = F + j + i, *p = w, tp;
            for(int k = 0; k < i; ++k, ++l, ++r, p += d)
                tp = *r * *p, *r = *l - tp, *l = *l + tp;
        }
}
void DFT() {
    static cp C[N];
    for(int i = 0;i < L; ++i) C[i] = cp(a[i], b[i]);
    FFT(C);
    for(int i = 0;i < L; ++i) {
        int j = L - i & L - 1;
        A[i] = (C[i] + conj(C[j])) * cp(0.5, 0.0);
        B[i] = (C[i] - conj(C[j])) * cp(0.0, -0.5);
    }
}
int main() {
    scanf("%s", s + 1); int n = strlen(s + 1); Pre(n << 1); 
    for(int i = 1;i <= n; ++i) a[n - i] = (s[i] == '1'), b[i] = (s[i] == '0');
    DFT(); for(int i = 0;i < L; ++i) A[i] = A[i] * B[i];
    FFT(A); for(int i = 1;i < L >> 1; ++i) std::swap(A[i], A[L - i]);
    for(int i = 1;i <= n; ++i) d[i] = (int)(A[n + i].r / L + 0.5) | (int)(A[n - i].r / L + 0.5);
    for(int i = n; i; --i) if(!d[i])
        for(int j = i;j <= n; j += i) if(d[j]) {d[i] = 1; break;}
    long long Ans = 0;
    for(int i = 1;i <= n; ++i) if(!d[n - i]) Ans ^= 1LL * i * i;
    printf("%lld\n", Ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值