题意:用m种不同颜色的珠子连成一条长为n的项链,其中,有k对珠子不能相邻,问总共有多少种(mod 9973)n<10^9,m<=10
题解:组合计数也就burning和polya了,这题用的是Burning Side。
考虑在一种置换f下的稳定核方法,由于只有旋转对称,如果是旋转k个珠子,那么稳定核的循环节也就是gcd(n,k)=r,枚举k的话是不现实的,那么只有枚举r,即n的所有约数。gcd(n,k)=r,即gcd(n/r,k/r)=1,也就是与n/r互质的数的个数(欧拉函数)就是循环节为r的置换个数。
对循环节为r的情况,需要考虑循环节内部珠子的排列,使它们满足题目要求,还要考虑第一个珠子与最后一个珠子是否满足要求(最后一个珠子的下一个珠子也是下一轮的第一个珠子),由于珠子种类只有10,可以用邻接矩阵map[i][j]表示i,j两种珠子是否能相邻,如果能,map[i][j]=1,反之,map[i][j]=0,这样的话,离散数学老师应该说过,如果用0,1矩阵A来表示无向图的连通情况的话,A^k代表的就是一个点经过k条路后能到达的地方的方法数。
因此,对于循环节为r的情况,A^r就是任意点经过r条路能到达的地方,与之对应的map[i][i]就是一个珠子经过可行路径转了r条路径又回到自己的种数,其实,就是前面说的满足题意的排列数,矩阵乘法可以分治加个速,对于n的约数随便求一下,这道题就出来了。
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; const int mr=100000; const LL mod=9973; bool notp[mr]; int pr[mr],fac[102],num[102]; int pn,top,n,m; LL ans; struct MAT { LL bas[13][13]; void init() { memset(bas,0,sizeof(bas)); } } mat[50]; MAT mul(MAT a,MAT b) { MAT c; c.init(); for(int i=1; i<=m; i++) for(int k=1; k<=m; k++) { if(a.bas[i][k]) { for(int j=1; j<=m; j++) { c.bas[i][j]+=a.bas[i][k]*b.bas[k][j]; if(c.bas[i][j]>=mod) c.bas[i][j]%=mod; } } } return c; } void getpri()//筛素数 { pn=0; memset(notp,0,sizeof(notp)); for(int i=2; i<mr; i++) { if(!notp[i]) { pr[pn++]=i; } for(int j=0; j<pn && i*pr[j]<mr; j++) { int k=i*pr[j]; notp[k]=1; if(i%pr[j]==0)break; } } } void divn() { int nn=n; top=0; int lim=(int)sqrt((double(nn)))+1; for(int i=0; pr[i]<=lim; i++) { if(nn%pr[i]==0) { fac[top]=pr[i]; num[top]=0; while(nn%pr[i]==0) num[top]++,nn/=pr[i]; top++; } } if(nn>1) fac[top]=nn,num[top++]=1; } int phi(int x) { int i, res=x; for (i=0;pr[i]<(int)sqrt((double)x)+1;i++) if(x%pr[i]==0) { res=res/pr[i]*(pr[i]-1); while(x%pr[i]==0)x/=pr[i]; } if(x>1)res=res/x*(x-1); return res; } void solve(int r) { int res=phi(n/r); MAT mt; mt.init(); for(int i=1;i<=m;i++) mt.bas[i][i]=1; for(int i=1,tp=r;tp;i++,tp>>=1) if(tp&1)mt=mul(mt,mat[i]); for(int i=1;i<=m;i++) { ans+=mt.bas[i][i]*res; if(ans>=mod)ans%=mod; } } void dfs(int id,int sum) { if(id==top) { solve(sum); return; } else { dfs(id+1,sum); for(int ct=0; ct<num[id]; ct++) dfs(id+1,sum=sum*fac[id]); } } void init() { for(int i=2; i<50; i++) mat[i]=mul(mat[i-1],mat[i-1]); } int Egcd (int a,int b, int &x, int &y) { if (b==0) { x=1,y=0; return a; } LL d, tp; d = Egcd (b, a%b, x, y); tp = x; x = y; y = tp - a/b*y; return d; } int getni() { int x,y; Egcd(n,mod,x,y); return (x%mod+mod)%mod; } int main() { getpri(); int T; for(scanf("%d",&T); T; T--) { int k; scanf("%d%d%d",&n,&m,&k); ans=0; for(int i=1; i<=m; i++) for(int j=1; j<=m; j++) mat[1].bas[i][j]=1; for(int a,b,i=0; i<k; i++) { scanf("%d%d",&a,&b); mat[1].bas[a][b]=mat[1].bas[b][a]=0; } init(); divn(); dfs(0,1); printf("%d\n",ans*getni()%mod); } return 0; }