zoukankan      html  css  js  c++  java
  • #6436. 「PKUSC2018」神仙的游戏

    题目描述

    小 D 和小 H 是两位神仙。他们经常在一起玩神仙才会玩的一些游戏,比如「口算一个 $4$ 位数是不是完全平方数」。

    今天他们发现了一种新的游戏:首先称 $s$ 长度为 $ m len$ 的前缀成为 border 当且仅当 $s[1dots ext {len} ] = s[|s|- ext {len} + 1dots |s|]$ 。给出一个由 01? 组成的字符串 $s$, 将 $s$ 中的问号用变成 01 替换,对每个 $ m len$ 口算是否存在替换问号的方案使得 $s$ 长度为 $ m len$ 的前缀成为 border,把这个结果记做 $f( ext{len})in {0,1}$。$f( ext{len}) = 1$ 如果 $s$ 长度为 $ m len$ 的前缀能够成为 border,否则 $f( ext{len}) = 0$。

    由于小 D 和小 H 是神仙,所以他们计算的 $s$ 的长度很长,因此把计算的结果一一比对会花费很长的时间。为了方便比对,他们规定了一个校验值:$(f(1) imes 1^2)~ ext{xor}~(f(2) imes 2^2)~ ext{xor}~(f(3) imes 3^2)~ ext{xor}~dots~ ext{xor}~(f(n) imes n^2)$ 来校验他们的答案是否相同。xor 表示按位异或。但是不巧,在某一次游戏中,他们口算出的校验值并不一样,他们希望你帮助他们来计算一个正确的校验值。当然,他们不强迫你口算,可以编程解决。

    数据范围

    $lvert s vert leq 5 imes 10^5$

    题解

    我们知道,如果一个前缀长度为 $len$ ,它能够成为 border 的前提是 $s_i=s_{i+n-len}$

    所以如果 $s_i e s_{i+x}$ ,那对于 $y|x$ ,长度为 $n-y$ 的前缀一定不是 border

    上述式子下标的差是定值,于是我们可以把其中一项下标翻转就是和为定值的式子啦

    于是我们设 $f_i=[s_i==0],g_i=[s_i==1]$ ,将 $g$ 翻转后卷积,得到哪些x是有出现不相等的情况,对于每个n-y的前缀,我们只需要判断y的倍数是否有出现不相等的情况即可

    效率: $O(n(logn+lnn))$

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=2e6+5,P=998244353;
    int A[N],B[N],n,t=1,p,r[N],G[2]={3,(P+1)/3};
    char s[N];long long ans;
    int X(int x){return x>=P?x-P:x;}
    int K(int x,int y){
        int z=1;
        for (;y;y>>=1,x=1ll*x*x%P)
            if (y&1) z=1ll*z*x%P;
        return z;
    }
    void Ntt(int *a,int o){
        for (int i=0;i<t;i++)
            if (i<r[i]) swap(a[i],a[r[i]]);
        for (int wn,i=1;i<t;i<<=1){
            wn=K(G[o],(P-1)/(i<<1));
            for (int x,y,j=0;j<t;j+=(i<<1))
                for (int w=1,k=0;k<i;k++,w=1ll*w*wn%P)
                    x=a[j+k],y=1ll*w*a[i+j+k]%P,
                    a[j+k]=X(x+y),a[i+j+k]=X(x-y+P);
        }
        if (o)
            for (int i=0,v=K(t,P-2);i<t;i++)
                a[i]=1ll*a[i]*v%P;
    }
    int main(){
        scanf("%s",s);n=strlen(s);
        for (;t<n+n;t<<=1,p++);
        for (int i=0;i<t;i++)
            r[i]=(r[i>>1]>>1)|((i&1)<<(p-1));
        for (int i=0;i<n;i++)
            A[i]=(s[i]==48),B[i]=(s[n-i-1]==49);
        Ntt(A,0);Ntt(B,0);
        for (int i=0;i<t;i++)
            A[i]=1ll*A[i]*B[i]%P;
        Ntt(A,1);ans=1ll*n*n;
        for (int i=1;i<n;i++){
            bool J=0;
            for (int j=i;j<n;j+=i)
                J|=(A[n-j-1] || A[n+j-1]);
            if (!J) ans^=1ll*(n-i)*(n-i);
        }
        cout<<ans<<endl;return 0;
    }
  • 相关阅读:
    [LeetCode]Reverse Linked List II
    [LeetCode]Move Zeroes
    Next Greater Element I
    Keyboard Row
    Number Complement
    SQL语句学习(二)
    SQL语句学习(一)
    jQuery学习(三)
    jQuery学习(二)
    JQuery学习(一)
  • 原文地址:https://www.cnblogs.com/xjqxjq/p/12247031.html
Copyright © 2011-2022 走看看