题目:用m种颜色给长度为n的项链染色,其中k对颜色不能相邻,旋转相同看作同一种方案,问方案数,结果模9973 。
思路:对于一段上的方案数是,dp[i][j]=sigma(dp[i-1][k]) (j与k可以相邻);长度非常大的时候这是做不了的,看到这个形式就可以考虑一下矩阵快速幂了。然后应用polya计数。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int mod = 9973;
struct node{
int m[11][11];
node()
{
memset(m,0,sizeof(m));
}
};
int m;
node Mul(node a,node b)
{
node c;
for(int i=0;i<m;i++)
{
for(int k=0;k<m;k++)
if(a.m[i][k])
{
for(int j=0;j<m;j++)
{
c.m[i][j]+=a.m[i][k]*b.m[k][j]%mod;
c.m[i][j]%= mod;
}
}
}
return c;
}
node Pow(node t,int p)
{
node ans;
for(int i=0;i<m;i++)
ans.m[i][i]=1;
while(p)
{
if(p&1)
ans=Mul(ans,t);
t=Mul(t,t);
p>>=1;
}
return ans;
}
node s;
int f(int n)
{
node ans=Pow(s,n);
int res=0;
for(int i=0;i<m;i++)
res=(res+ans.m[i][i])%mod;
return res;
}
int qmod(int x,int p)
{
int ans=1;
x%=mod;
while(p)
{
if(p&1)
ans=ans*x%mod;
x=x*x%mod;
p>>=1;
}
return ans;
}
const int MAXN=100010;
int prime[MAXN+5];
void getPrime()
{
memset(prime,0,sizeof(prime));
for(int i=2;i<=MAXN;i++)
{
if(!prime[i])prime[++prime[0]]=i;
for(int j=1;j<=prime[0]&&prime[j]<=MAXN/i;j++)
{
prime[prime[j]*i]=1;
if(i%prime[j]==0) break;
}
}
}
int eular(int x)
{
int r=x;
for(int i=1;prime[i]*prime[i]<=x&&i<=prime[0];i++)
{
if(x%prime[i]==0)
{
r=r-r/prime[i];
while(x%prime[i]==0)
x/=prime[i];
}
}
if(x>1)r=r-r/x;
return r%mod;
}
int n,t,k;
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++)
s.m[i][j]=1;
for(int i=1;i<=k;i++)
{
int x,y;
scanf("%d%d",&x,&y);
x--,y--;
s.m[x][y]=s.m[y][x]=0;
}
int ans=0;
for(int i=1;i*i<=n;i++)
{
if(n%i==0)
{
ans=(ans+f(i)*eular(n/i)%mod)%mod;
if(i*i!=n)
ans=(ans+f(n/i)*eular(i)%mod)%mod;
}
}
ans=ans*qmod(n,mod-2)%mod;
printf("%d\n",ans);
}
return 0;
}