BZOJ3457 : Ring
根据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; }