zoukankan      html  css  js  c++  java
  • LOJ6436 神仙的游戏

    LOJ6436 神仙的游戏

    题目传送门

    题意

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

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

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

    题解

    简单的观察可知如果一种长度的border是可行的当且仅当这个字符串中循环节长度为(n-len)的字符串是相同的(问号可匹配仍以一个字符),于是我们就有了一种朴素的方法就是枚举border的长度然后(O(n))进行判断。考虑优化这个算法,我们可以记数组(a)表示字符串(s)中1出现的位置,然后记数组(b)表示字符串中0的位置。如果我们将b数组reverse一下,就可以发现如果将这两个数组相乘,那么乘除来的数组内,如果某一位是1,那么说明字符串(s)中存在(i)(j)的字符是不同的,这样我们就可以求出他们的最大循环节len。还有一个就是如果某一个循环节len是不可行,那么循环节(len')满足(len'|len)则也是不可行的,这样就可以在(O(nlog(n)))的时间内求解。

    Code:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int Md=998244353;
    const int N=5e5+500;
    char s[N];
    int vis[N];
    vector<int>a,b;
    
    namespace NTT {
      inline int Mul(const int &x,const int &y) {return (ll)x*y%Md;}
      inline int Add(const int &x,const int &y) {return (x+y)>=Md?(x+y-Md):(x+y);}
      inline int Sub(const int &x,const int &y) {return (x-y)<0?(x-y+Md):(x-y);}
      int rev[N<<2|1];
      int Powe(int x,int y) {
        int ans=1;
        while(y) {
          if(y&1) ans=Mul(ans,x);
          x=Mul(x,x);
          y>>=1;
        }
        return ans;
      }
    
      void DFT(vector<int>&a,int len) {
        for(int i=0;i<len;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
        for(int i=1;i<len;i<<=1) {
          int wn=Powe(3,(Md-1)/(i<<1));
          for(int j=0;j<len;j+=i<<1) {
    	    int nw=1,x,y;
    	    for(int k=0;k<i;k++,nw=Mul(nw,wn)) {
    	      x=a[j+k],y=Mul(a[i+j+k],nw);
    	      a[j+k]=Add(x,y);a[i+j+k]=Sub(x,y);
    	    }
          }
        }
      }
    
      void IDFT(vector<int>&a,int len) {
        reverse(a.begin()+1,a.end());
        DFT(a,len);
        int Inv=Powe(len,Md-2);
        for(int i=0;i<len;i++) a[i]=Mul(a[i],Inv);
      }
    
      vector<int> MUL(vector<int>a,vector<int>b) {
        int len,n=a.size(),m=b.size();
        for(len=1;len<n+m-2;len<<=1);
        a.resize(len),b.resize(len);
        for(int i=0;i<len;i++) {
          rev[i]=(rev[i>>1]>>1)|(i&1?len>>1:0);
        }
        DFT(a,len);DFT(b,len);
        for(int i=0;i<len;i++) a[i]=Mul(a[i],b[i]);
        IDFT(a,len);
        return a;
      }
    }
    
    int main() {
      scanf("%s",s);
      int n=strlen(s);
      a.resize(n),b.resize(n+1);
      for(int i=0;i<n;i++) {
        if(s[i]=='0') a[i]=1;
        if(s[i]=='1') b[i]=1;
      }
      reverse(b.begin(),b.end());
      a=NTT::MUL(a,b);
      ll ret=(ll)n*n;
      for(int i=1;i<n;i++) {
        int f=1;
        int t=n-i;
        for(int j=t;j<n;j+=t) {
          if(a[n-j]||a[n+j]) f=0;
        }
        if(f) ret^=(ll)i*i;
      }
      printf("%lld
    ",ret);
      return 0;
    }
    
  • 相关阅读:
    无缝滚动效果实现
    js模拟滚动条插件一二
    powerDesigner15.1破解
    解决asp.net 中GridView中隐藏过长的数据(使用ToolTip)
    IE6下弹出div覆盖select下拉框
    sql中的常用函数结合实现截取替换功能
    【.NET重修计划】数组,集合,堆栈的问题
    Redis可视化工具推荐
    Redis中String类型的相关命令操作
    Redis的五大数据类型以及key的相关操作命令
  • 原文地址:https://www.cnblogs.com/Apocrypha/p/10153596.html
Copyright © 2011-2022 走看看