题意:用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;
}