给一串有n(1<=10^9 && gcd(n,9973))颗珠子的项链染色,已知某些{ci,cj}(i可能等于j)颜色不能出现在相连的珠子中。项链在平面内转动(不能翻转)得到的为等价方案,求不同的染色方案数mod 9973。
由于项链只能绕中心转动,而不能翻转,所以有一个很好的性质:项链转 2*PI*k/n个角度的置换共有 gcd(k,n)个长度均为len=n/gcd(k,n)的循环节,而且可以发现,若gcd(k,n)>1,则从某一个编号的项链开始顺时针(或逆时针)相邻的len个珠子两两处于不同的循环节中。这样k置换(1<=k<=n)的固定构型数等价于:
给长度为gcd(k,n)的序列染色(没写错,确实是序列),其中所有相邻编号(beg和end为相邻的)为合法对(若color i 和 color j 能够分别出现在相邻的两珠子上,那么说(i,j)是合法对)的染色方案。
假设一个长度为 L 的序列的合法表示为 a1,a2,……,aL 那么(a1,a2)(a2,a3),……,(a(L-1),aL),(aL,a1)必定都为1 ,所以令 g[ai,aj]=1表示(ai,aj)为合法对,g[ai,aj]=0表示(ai,aj)为非法对。那么以a1开头的合法序列为k个那么必定存在且仅存在k个不同的序列{b1,b2,b3,b4,……,b(L-1)}满足 g[a1,b1]*g[b1,b2]*……g[b(L-2),b(L-1)]*g[b(L-1),a1]=1; 这样很显然就是矩阵g[ai,aj]的L次幂中g[a1,a1]的的值。
很显然,这题可以用矩阵快速幂取模。
1 #include <iostream>
2 #include <cstring>
3 #include <cstdio>
4 using namespace std;
5
6 const int max_n=15;
7 const int MOD=9973;
8 int N;
9
10 struct Mat{
11 int mat[max_n][max_n];
12 };
13
14 Mat operator*(Mat a,Mat b){
15 Mat c;
16 memset(c.mat,0,sizeof(c.mat));
17 for(int i=0;i<N;i++){
18 for(int j=0;j<N;j++){
19 for(int k=0;k<N;k++){
20 if(a.mat[i][k] && b.mat[k][j]){
21 c.mat[i][j]=(c.mat[i][j]+a.mat[i][k]*b.mat[k][j])%MOD;
22 }
23 }
24 }
25 }
26 return c;
27 }
28
29 Mat operator^(Mat A,int x){
30 Mat c;
31 memset(c.mat,0,sizeof(c.mat));
32 for(int i=0;i<N;i++) c.mat[i][i]=1;
33 for(;x;x>>=1){
34 if(x&1){
35 c=c*A;
36 }
37 A=A*A;
38 }
39 return c;
40 }
41
42 int mypow(int a,int b){
43 int res=1;
44 for(;b;b>>=1){
45 if(b&1){
46 res*=a; res%=MOD;
47 }
48 a*=a; a%=MOD;
49 }
50 return res;
51 }
52
53 int M,K;
54
55 int PHI(int n){
56 int i,res=n; long long j;
57 for(i=2,j=4LL;j<=(long long)n;i++,j+=i+i-1){
58 if(!(n%i)){
59 res=res/i*(i-1);
60 while(!(n%i)) n/=i;
61 }
62 }
63 if(n>1) res=res/n*(n-1);
64 return res%MOD;
65 }
66
67 Mat A;
68 int solve(int p){
69 int res=0;
70 Mat C=A^p;
71 for(int i=0;i<N;i++){
72 res+=C.mat[i][i]; res%=MOD;
73 }
74 return res;
75 }
76 int main()
77 {
78 int T;
79
80 scanf("%d",&T);
81
82 for(int ncas=1;ncas<=T;ncas++){
83 scanf("%d%d%d",&M,&N,&K);
84 int u,v;
85 for(int i=0;i<N;i++){
86 for(int j=0;j<N;j++){
87 A.mat[i][j]=1;
88 }
89 }
90 for(int i=0;i<K;i++){
91 scanf("%d%d",&u,&v);
92 A.mat[u-1][v-1]=A.mat[v-1][u-1]=0;
93 }
94
95 int ans=0;
96
97 for(int p=1;p*p<=M;p++){
98 if(M%p==0){
99 if(p*p==M){
100 ans = (ans+ PHI(p)*solve(p) )%MOD;
101 }else{
102 ans = (ans+ PHI(p)*solve(M/p)+ PHI(M/p)*solve(p) )%MOD;
103 }
104 }
105 }
106
107 int inv=mypow((M%MOD),MOD-2); //晕,这里没取模导致超出整形,WA了三次
108 ans*=inv; ans%=MOD; //逆元。
109 printf("%d\n",ans);
110 }
111 return 0;
112 }
113