Burnside引理+欧拉函数+矩阵乘法。
突然发现Burnside引理忘得差不多了(根本没学会好吗)
今天才知道不动点原来是这么求的,以前都是循环k=1->n,ans+=calc(gcd(n,k))
现在反过来,令gcd(n,k)=r,ans+=calc(r)*euler(n/r),显然gcd(n/r,k/r)=1,所以循环节为r的循环一共有euler(n/r)个。
然后还有就是计数其实相当于在有m个顶点的图上走,所以设邻接矩阵g[i][j]表示从i走到j有g[i][j]种走法(本题显然就一种),然后g^k就可以求出从i走k步到j有几种走法,矩阵快速幂一下就好了。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int p=9973; const int N=10+2; int qmul(int a,int b){ int ans=1; a%=p; for(;b;b>>=1,a=a*a%p)if(b&1) ans=ans*a%p; return ans; } int inv(int x){ return qmul(x,p-2); } int phi(int x){ int ans=x; for(int i=2;i*i<=x;i++) if(x%i==0){ while(x%i==0)x/=i; ans=ans/i*(i-1); } if(x>1)ans=ans/x*(x-1); return ans%p; } int m; struct Matrix{ int a[N][N]; Matrix(){ memset(a,0,sizeof(a)); } }; Matrix operator * (Matrix a,Matrix b){ Matrix c; for(int i=1;i<=m;i++) for(int k=1;k<=m;k++) if(a.a[i][k]) for(int j=1;j<=m;j++) if(b.a[k][j]) c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%p; return c; } Matrix operator ^ (Matrix a,int k){ Matrix ans; for(int i=1;i<=m;i++)ans.a[i][i]=1; for(;k;k>>=1,a=a*a)if(k&1)ans=ans*a; return ans; } Matrix A,B; int calc(int x){ B=A^x; int ans=0; for(int i=1;i<=m;i++) ans=(ans+B.a[i][i])%p; return ans; } int solve(int n){ int ans=0; for(int i=1;i*i<=n;i++) if(n%i==0){ ans=(ans+phi(i)*calc(n/i)%p)%p; if(n/i!=i)ans=(ans+phi(n/i)*calc(i)%p)%p; } ans=ans*inv(n)%p; return ans; } int main(){ //freopen("a.in","r",stdin); int T;scanf("%d",&T); while(T--){ int n,k;scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=m;i++) for(int j=1;j<=m;j++)A.a[i][j]=1; while(k--){ int u,v;scanf("%d%d",&u,&v); A.a[u][v]=A.a[v][u]=0; } printf("%d\n",solve(n)); } return 0; }