LOJ6436「PKUSC2018」神仙的游戏(FFT)

传送门:https://loj.ac/problem/6436


考虑没有问号的情况。
直接 k m p kmp kmp即可
由于 k m p kmp kmp不好处理 ? ? ?间接矛盾的情况,我们直接讲用生成函数处理没有问号的情况的做法。

对于一个 b o r d e r border border,如果前缀后缀首位置之差为 t t t,显然我们将一个串翻转后每一个位置赋值,再做卷积,那么一个 b o r d e r border border对应的位置都会被卷到同一个位置。即对于原串中位置 i , j ( i &lt; j ) i,j(i&lt;j) i,j(i<j),翻转后卷积会被卷到 i + n − j i+n-j i+nj,即 n − ( j − i ) n-(j-i) n(ji)
即所有不同的 b o r d e r border border会在卷积中根据 b o r d e r border border首位置之差分类

我们考虑一个生成函数
在原串中 A ( x ) = ∑ i = 1 n [ s [ i ] = = ′ 1 ′ ] x i A(x)=\sum_{i=1}^{n} [s[i] == &#x27;1&#x27;]x^i A(x)=i=1n[s[i]==1]xi
翻转后 B ( x ) = ∑ i = 1 n [ s [ i ] = = ′ 0 ′ ] x i B(x) = \sum_{i=1}^{n} [s[i]==&#x27;0&#x27;]x^i B(x)=i=1n[s[i]==0]xi
如果没有问号,直接卷起来后判断某个位置是否有系数即可

考虑问号的情况
问号导致的矛盾必然是前缀后缀相交, ? ? ?出现在两个位置间接矛盾,如 1 = ? = 0 1=?=0 1=?=0
考虑这种情况,显然这个 b o r d e r border border如果满足条件必然也满足最小循环串

C ( x ) = A ( x ) B ( x ) C(x) =A(x)B(x) C(x)=A(x)B(x) C ( x ) C(x) C(x)中某个位置有值得含义即为某对长度为固定值的 ( 0 , 1 ) (0,1) (0,1)产生了矛盾。

可以发现 ? ? ?出现矛盾可以等价于通过最小循环串连接了一对矛盾的 ( 0 , 1 ) (0,1) (0,1)

也就是对于一个长度为 l e n len len的串 S S S,他的最小循环串长度为 x = n − l e n x=n-len x=nlen
那么通过 ? ? ?矛盾当且仅当 x ∣ t ( [ t ] C ( x ) ! = 0 ) x|t([t]C(x)!=0) xt([t]C(x)!=0)
F F T FFT FFT卷积做完之后直接对某个串暴力循环枚举倍数
复杂度 O ( n l o g n + n l n n ) O(nlogn+nlnn) O(nlogn+nlnn)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back 
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long
#define Comp complex<double>
#ifdef M_pi
const double pi = M_pi;
#else
const double pi = acos(-1.0);
#endif

int len;
int n , m;
char s[501000];
Comp a[2001000] , b[2001000];
int r[2001000];
int flen;
int rd()
{
    int sum = 0;char c = getchar();bool flag = true;
    while(c < '0' || c > '9') {if(c == '-') flag = false;c = getchar();}
    while(c >= '0' && c <= '9') sum = sum * 10 + c - 48,c = getchar();
    if(flag) return sum;
    else return -sum;
}
void dft(Comp *a ,int f)
{
    for(int i = 0; i < flen; ++i)
        if(i < r[i]) swap(a[i], a[r[i]]);
    for(int len = 2;len <= flen;len <<= 1)
    {
        Comp wn = Comp( cos(2 * pi / len) , f * sin(2 * pi / len));
        for(int k = 0;k <= flen / len - 1;++k)
        {
            int st = k * len;
            Comp w = 1;
            for(int i = 0;i < (len>>1);++i)
            {
                Comp x = a[st + i],
                    y = a[st + i + len / 2] * w;
                a[st + i] = x + y;
                a[st + i + len / 2] = x - y;
                w *= wn;
            }
        }
        
    } 
    return;
}   
bool flag[1001000];
void init(){
    n = m = len-1;
    for(int i = 0 ;i <= n;++i) a[i] = s[i+1] == '1';
    for(int i = 0 ;i <= m;++i) b[i] = s[n-i+1] == '0';
    flen = 1;
    while( flen < (n + m + 1)) flen *= 2;
    for(int i = n + 1;i < flen;++i) a[i] = 0;
    for(int i = m + 1;i < flen;++i) b[i] = 0;  
    int l = log2(flen);
    for(int i = 0;i < flen;++i)
        r[i] = (r[i>>1]>>1)|((i&1)<<(l-1));
    dft(a,1);
    dft(b,1);
    for(int i = 0;i < flen;++i) a[i] *= b[i];
    dft(a,-1);
    for(int i = 0;i <= n+m;++i)
    {
        int tmp = (int)(a[i].real()/flen + 0.5);
        if(tmp != 0 && i < len) flag[len-(i+1)] = true;
        if(tmp != 0 && i >= len) flag[(i+1)-len] = true;
    }
    return;
}
int main()
{
    scanf("%s",s+1);len = strlen(s+1);
    init();
    ll ans = 1ll*len*len;
    rep(i,1,len-1)
    {
        int tmp = len-i,now = tmp;
        bool ck = true;
        while(now <= len)
        {
            if(flag[now]) {ck = false;break;}
            now += tmp; 
        } 
        if(ck)ans = ans^(1ll*i*i);
    }
    printf("%lld\n",ans);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值