BZOJ3457 : Ring

Claris發表於2018-03-11

根據Polya定理:

\[ans=\frac{\sum_{d|n}\varphi(d)cal(\frac{n}{d})}{n}\]

其中$cal(n)$表示長度為$n$的無限迴圈後包含$S$的串的數量。

對於$cal(n)$的計算,考慮用總方案數$2^n$減去單次迴圈內不包含$S$的方案數。

列舉進入迴圈時與$S$的KMP指標$k$,然後設$f[i][j]$表示考慮前$i$個位置,KMP指標為$j$的方案數,最終結果為$f[n][k]$。

轉移可以用矩陣$G$表示,預處理出$G$的$2^0,2^1,...,2^{\log n}$次方,那麼每次計算$cal(n)$只需要進行$O(k\log n)$次$O(k^2)$的矩陣乘向量。

總時間複雜度$O(d(n)k^3\log n)$。

 

#include<cstdio>
#define rep(i) for(int i=0;i<m;i++)
const int N=35,P=1000000007;
int n,m,i,j,k,nxt[N],g[N][2],e[N][N][N],c[N][N],f[N],h[N],ans;char a[N];
inline void mul(int a[][N],int f[][N]){
  rep(i)rep(j)c[i][j]=0;
  rep(i)rep(j)if(a[i][j])rep(k)if(a[j][k])c[i][k]=(1LL*a[i][j]*a[j][k]+c[i][k])%P;
  rep(i)rep(j)f[i][j]=c[i][j];
}
inline void mulv(int a[][N]){
  rep(i){
    h[i]=0;
    rep(j)if(f[j]&&a[i][j])h[i]=(1LL*f[j]*a[i][j]+h[i])%P;
  }
  rep(i)f[i]=h[i];
}
inline int phi(int n){
  int t=1;
  for(int i=2;i<=n/i;i++)if(n%i==0){
    t*=i-1,n/=i;
    while(n%i==0)t*=i,n/=i;
  }
  if(n>1)t*=n-1;
  return t;
}
inline int po(int a,int b){int t=1;for(;b;b>>=1,a=1LL*a*a%P)if(b&1)t=1LL*t*a%P;return t;}
inline int cal(int n){
  int t=0;
  for(int i=0;i<m;i++){
    rep(j)f[j]=i==j;
    for(int j=0;(1LL<<j)<=n;j++)if(n>>j&1)mulv(e[j]);
    t=(t+f[i])%P;
  }
  return(po(2,n)-t+P)%P;
}
int main(){
  scanf("%d%d%s",&n,&m,a+1);
  for(i=1;i<=m;i++)a[i]=a[i]=='R';
  for(i=2;i<=m;nxt[i++]=j){
    while(j&&a[j+1]!=a[i])j=nxt[j];
    if(a[j+1]==a[i])j++;
  }
  rep(i)for(j=0;j<2;j++){
    for(k=i;k&&a[k+1]!=j;k=nxt[k]);
    if(a[k+1]==j)k++;
    g[i][j]=k;
  }
  rep(i)for(j=0;j<2;j++)e[0][g[i][j]][i]++;
  for(i=1;(1LL<<i)<=n;i++)mul(e[i-1],e[i]);
  for(i=1;i<=n/i;i++)if(n%i==0){
    ans=(1LL*phi(i)*cal(n/i)+ans)%P;
    if(i*i!=n)ans=(1LL*phi(n/i)*cal(i)+ans)%P;
  }
  ans=1LL*ans*po(n,P-2)%P;
  return printf("%d",ans),0;
}