题目大意:给定n(n <= 10^9)颗珠子和m种颜色,这n颗珠子可以组成一串项链,每颗珠子可以用m中颜色来填充,如果两种涂色方法通过旋转项链可以得到,则两种涂色方法等价。 现在再给定K组限制,每组限制a、b代表颜色a和颜色b不能涂在相邻的珠子上面。问一共有多少种涂色方法。
解题思路:这题和POJ2154 题目大致相同,不同的是这里多了一个限制条件,即可能两种颜色不能涂在相邻的珠子上面。对于这种情况我们可以通过矩阵连乘得到,先初始化矩阵 array[i][j]为1.如果颜色a和颜色b不能涂在相邻的珠子,那么array[a][b] = array[b][a] = 0; 对于具有n/L个循环节的置换(L为每个循环节的长度)。先求出array[][]的n/L次幂,然后将这个矩阵的array[i][i] (1<=i<=m)全部加起来即为这种置换下涂色不变的方法数。对于为什么这样做,仔细想一想就清楚了,其余的算法基本和POJ 2154相同,这里就不解释了。
#include <cstdlib>
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=11,mod=9973;
int n,m;
struct Matrix
{
int a[N][N];
int n;
Matrix operator*(Matrix l)
{
Matrix temp;
temp.n=n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
temp.a[i][j]=0;
for(int k=0;k<n;k++)
{
temp.a[i][j]+=a[i][k]*l.a[k][j];
temp.a[i][j]%=mod;
}
}
return temp;
}
}M;
Matrix pow(Matrix t,int x)
{
Matrix temp=t,ans;
while(x)
{
if(x&1) ans=ans*temp;
temp=temp*temp;
x>>=1;
}
return ans;
}
int phi(int n)
{
int ans=1;
for(int i=1;i*i<=n;i++)
{
if(n%i==0)
{
ans*=(i-1);
n/=i;
while(n%i==0)
{
ans*=i;
n/=i;
}
ans%=mod;
}
}
if(n>1) ans*=(n-1);
return ans%mod;
}
int gettr(int x)
{
Matrix t=pow(M,x);
int res=0;
for(int i=0;i<t.n;i++)
res=(res+t.a[i][i])%mod;
return res;
}
int main(int argc, char *argv[])
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
int k;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
M.a[i][j]=1;
scanf("%d",&k);
for(int i=0;i<k;i++)
{
int x,y;
scanf("%d%d",&x,&y);
M.a[x-1][y-1]=M.a[y-1][x-1]=0;
}
int ans=0;
for(int i=2;i*i<=n;i++)
if(n%i==0)
{
ans=(ans+gettr(i)*phi(n/i)%mod)%mod;
if(i*i<n)
ans=(ans+gettr(n/i)*phi(i)%mod)%mod;
}
cout<<ans<<endl;
int tt;
for(tt=1;;tt++)
if(((long long)n*tt-ans)%mod==0)
break;
printf("%d\n",tt%mod);
}
system("PAUSE");
return EXIT_SUCCESS;
}