相比于直接polya,多了相邻元素限制,也就是难度在于怎么求 对应置换不变的染色种类,我们考虑用个矩阵来村颜色之间的关系,如果是1就是可相邻,对于不可相邻的,赋值为1,这样对于长度为k的环,种类数就是,根据离散知识:矩阵k次幂,对角线元素之和(开始以为是k-1次幂所有元素之和,但这是个环,要绕回来的,所以是k次幂对角线元素之和)
然后就好搞了。搓比代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MOD = 9973;
typedef long long LL;
int m,k,n,a,b,T,mat[10][10],tmp[10][10],tp[10][10],prime[36000],is[36000];
void getprime()
{
int cnt=0;
for(int i=2;i<36000;i++)
{
if(!is[i])
{
prime[cnt++]=i;
for(int j=i;j<36000;j+=i)
is[j]=1;
}
}
}
int pow(int x,int y)
{
x=x%MOD;
int t=1;
while(y)
{
if(y&1)t=(t*x)%MOD;
x=(x*x)%MOD;
y>>=1;
}
return t;
}
void mul(int a[10][10],int b[10][10],int m)
{
int c[10][10];
memset(c,0,sizeof(c));
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
for(int k=0;k<m;k++)
c[i][j]=(c[i][j]+a[i][k]*b[k][j])%MOD;
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
a[i][j]=c[i][j];
}
int get(int x)
{
memset(tmp,0,sizeof(tmp));
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
tp[i][j]=mat[i][j];
for(int i=0;i<m;i++)tmp[i][i]=1;
while(x)
{
if(x&1)
mul(tmp,tp,m);
mul(tp,tp,m);
x>>=1;
}
int ans=0;
for(int i=0;i<m;i++)
ans=(ans+tmp[i][i])%MOD;
return ans;
}
int eular(int x)
{
if(x==1)return 1;
int rep=x;
for(int i=0;prime[i]*prime[i]<=x;i++)
{
if(x%prime[i]==0)
{
rep-=rep/prime[i];
x/=prime[i];
}
while(x%prime[i]==0)x/=prime[i];
if(x==1)break;
}
if(x!=1)
rep-=rep/x;
return rep%MOD;
}
int inv(int n)
{
return pow(n,MOD-2)%MOD;
}
int main()
{
scanf("%d",&T);
getprime();
while(T--)
{
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
mat[i][j]=1;
for(int i=0;i<k;i++)
{
scanf("%d%d",&a,&b);
a--;
b--;
mat[a][b]=mat[b][a]=0;
}
int ans=0;
int i;
for(i=1;i*i<n;i++)
{
if(n%i==0)
{
ans=(ans+(get(i)*eular(n/i))%MOD)%MOD;
ans=(ans+(get(n/i)*eular(i))%MOD)%MOD;
}
}
if(i*i==n)
ans=(ans+(get(i)*eular(n/i))%MOD)%MOD;
printf("%d\n",(ans*inv(n))%MOD);
}
return 0;
}