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;
}