POJ 2888 Magic Bracelet

Burnside引理+欧拉函数+矩阵乘法。

突然发现Burnside引理忘得差不多了(根本没学会好吗)

今天才知道不动点原来是这么求的,以前都是循环k=1->n,ans+=calc(gcd(n,k))

现在反过来,令gcd(n,k)=r,ans+=calc(r)*euler(n/r),显然gcd(n/r,k/r)=1,所以循环节为r的循环一共有euler(n/r)个。

然后还有就是计数其实相当于在有m个顶点的图上走,所以设邻接矩阵g[i][j]表示从i走到j有g[i][j]种走法(本题显然就一种),然后g^k就可以求出从i走k步到j有几种走法,矩阵快速幂一下就好了。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int p=9973;
const int N=10+2;
int qmul(int a,int b){
	int ans=1;
	a%=p;
	for(;b;b>>=1,a=a*a%p)if(b&1)
	ans=ans*a%p;
	return ans;
}
int inv(int x){
	return qmul(x,p-2);
}
int phi(int x){
	int ans=x;
	for(int i=2;i*i<=x;i++)
	if(x%i==0){
		while(x%i==0)x/=i;
		ans=ans/i*(i-1);
	}
	if(x>1)ans=ans/x*(x-1);
	return ans%p;
}
int m;
struct Matrix{
	int a[N][N];
	Matrix(){
		memset(a,0,sizeof(a));
	}
};
Matrix operator * (Matrix a,Matrix b){
	Matrix c;
	for(int i=1;i<=m;i++)
	for(int k=1;k<=m;k++)
	if(a.a[i][k])
	for(int j=1;j<=m;j++)
	if(b.a[k][j])
	c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%p;
	return c;
}
Matrix operator ^ (Matrix a,int k){
	Matrix ans;
	for(int i=1;i<=m;i++)ans.a[i][i]=1;
	for(;k;k>>=1,a=a*a)if(k&1)ans=ans*a;
	return ans;
}
Matrix A,B;
int calc(int x){
	B=A^x;
	int ans=0;
	for(int i=1;i<=m;i++)
	ans=(ans+B.a[i][i])%p;
	return ans;
}
int solve(int n){
	int ans=0;
	for(int i=1;i*i<=n;i++)
	if(n%i==0){
		ans=(ans+phi(i)*calc(n/i)%p)%p;
		if(n/i!=i)ans=(ans+phi(n/i)*calc(i)%p)%p;
	}
	ans=ans*inv(n)%p;
	return ans;
}
int main(){
	//freopen("a.in","r",stdin);
	int T;scanf("%d",&T);
	while(T--){
		int n,k;scanf("%d%d%d",&n,&m,&k);
		for(int i=1;i<=m;i++)
		for(int j=1;j<=m;j++)A.a[i][j]=1;
		while(k--){
			int u,v;scanf("%d%d",&u,&v);
			A.a[u][v]=A.a[v][u]=0;
		}
		printf("%d\n",solve(n));
	}
	return 0;
}


你可能感兴趣的:(POJ 2888 Magic Bracelet)