POJ 2888 Magic Bracelet(Burnside引理+欧拉函数+矩阵快速幂+逆元)

Magic Bracelet
Time Limit: 2000MS Memory Limit: 131072K
Total Submissions: 5749 Accepted: 1850

Description

Ginny’s birthday is coming soon. Harry Potter is preparing a birthday present for his new girlfriend. The present is a magic bracelet which consists of n magic beads. The are m kinds of different magic beads. Each kind of beads has its unique characteristic. Stringing many beads together a beautiful circular magic bracelet will be made. As Harry Potter’s friend Hermione has pointed out, beads of certain pairs of kinds will interact with each other and explode, Harry Potter must be very careful to make sure that beads of these pairs are not stringed next to each other.

There infinite beads of each kind. How many different bracelets can Harry make if repetitions produced by rotation around the center of the bracelet are neglected? Find the answer taken modulo 9973.

Input

The first line of the input contains the number of test cases.

Each test cases starts with a line containing three integers n (1 ≤ n ≤ 109gcd(n, 9973) = 1), m (1 ≤ m ≤ 10), k (1 ≤ k ≤ m(m − 1) ⁄ 2). The next k lines each contain two integers a and b (1 ≤ ab ≤ m), indicating beads of kind a cannot be stringed to beads of kind b.

Output

Output the answer of each test case on a separate line.

Sample Input

4
3 2 0
3 2 1
1 2
3 2 2
1 1
1 2
3 2 3
1 1
1 2
2 2

Sample Output

4
2
1
0

Source

[Submit]   [Go Back]   [Status]   [Discuss]

Home Page   Go Back  To top


题意:n个珠子组成项链,m种颜色为其染色,然而有k种关系限制。即颜色x和颜色y不能相邻。

题解:假如没有颜色不相邻限制的话就是polya定理计数模板题了,有了限制的话我们就不能用polya定理了。

呢该怎么办呢?用了半天容斥原理发现根本不对。。看了题解,真的是神题。。。。。

既然polya定理不适用,呢我们就要考虑Burnside引理了(换着用?)

我们首先回顾一下burnside引理:一个本质不同的组合数等于所有置换下不动点的个数之和除以置换种类数。

因为这题只有旋转,并且项链的旋转是有规律的,不难发现,每一个循环节中相邻的珠子的距离是相同的。

可是这和解这道题有关系吗?先别着急,我们往下走,此时我们可以求出同一个循环节中相邻珠子的距离一定为

gcd(i,n),假如我们从任意一个珠子开始顺时针数起,这gcd(n,i)个珠子一定在不同循环节中。还是呢句话,这和这道题有关系吗?   说实话,不看题解我是真的无从下手,题解也是看了将近3个小时。。越看越迷。。。

但其实想到这里问题已经解决了不是吗?根据burnside引理,不就是求长度为gcd(i,n)的珠子的染色数嘛。。

剩下的问题就好办了,对于gcd(i,n),我们可以用欧拉函数降低复杂度,然后对于合法染色数,我们可以考虑用矩阵优化,因为最后要除以n,而n又很大,我们可以让其乘上n的逆元即可。。。

#include<set>      
#include<map>         
#include<stack>                
#include<queue>                
#include<vector>        
#include<string>     
#include<time.h>    
#include<math.h>                
#include<stdio.h>                
#include<iostream>                
#include<string.h>                
#include<stdlib.h>        
#include<algorithm>       
#include<functional>        
using namespace std;                
#define ll long long          
#define inf 1000000000         
#define mod 9973               
#define maxn  50005    
#define lowbit(x) (x&-x)                
#define eps 1e-9   
int a[maxn]={1,1},b[maxn],cnt; 
struct node
{
	ll x[15][15];
}aa;
void init()
{
	ll i,j;
	for(i=2;i<maxn;i++)  
    {  
        if(a[i])  
            continue;  
        b[++cnt]=i;  
        for(j=i*i;j>0 && j<maxn;j+=i)  
            a[j]=1;  
    }  
}
ll q(ll x,ll y)    
{    
    ll res=1;    
    while(y)    
    {    
        if(y%2)    
            res=res*x%mod;    
        x=x*x%mod;    
        y/=2;    
    }    
    return res;    
} 
node q2(node a,node b,ll m)
{
	ll i,j,k;
	node c;
	for(i=1;i<=m;i++)
		for(j=1;j<=m;j++)
		{
			c.x[i][j]=0;
			for(k=1;k<=m;k++)
				c.x[i][j]=c.x[i][j]+a.x[i][k]*b.x[k][j];
			c.x[i][j]%=mod;
		}
	return c;
}
node q1(node a,ll y,ll m)
{
	node b;
	int i,j;
	for(i=1;i<=m;i++)
		for(j=1;j<=m;j++)
		{
			if(i==j)
				b.x[i][j]=1;
			else
				b.x[i][j]=0;
		}
	while(y)
	{
		if(y%2)
			b=q2(b,a,m);
		a=q2(a,a,m);
		y/=2;
	}
	return b;
}
ll ol(ll x)    
{    
    ll i,res=x;    
    for(i=1;i<=cnt && b[i]*b[i]<=x;i++)    
    {    
        if(x%b[i]==0)    
        {    
            res=res-res/b[i];    
            while(x%b[i]==0)    
                x/=b[i];    
        }    
    }    
    if(x>1)    
        res=res-res/x;    
    return res;    
}   
ll work(node a,ll m)
{
	ll ans=0;
	int i;
	for(i=1;i<=m;i++)
		ans=(ans+a.x[i][i])%mod;
	return ans;
}
int main(void)    
{    
	int t;
	init();
    ll n,m,i,j,k,x,y;    
	scanf("%d",&t);
    while(t--)    
    {   
		scanf("%lld%lld%lld",&n,&m,&k);
		for(i=1;i<=m;i++)
			for(j=1;j<=m;j++)
				aa.x[i][j]=1;
		for(i=1;i<=k;i++)
		{
			scanf("%lld%lld",&x,&y);
			aa.x[x][y]=aa.x[y][x]=0;
		}
		ll ans=0;
		for(i=1;i*i<=n;i++)
		{
			if(n%i==0)
			{
				node tmp;
				tmp=q1(aa,i,m);
				ans=(ans+ol(n/i)%mod*work(tmp,m)%mod)%mod;
				if(i*i==n)
					break;
				tmp=q1(aa,n/i,m);
				ans=(ans+ol(i)%mod*work(tmp,m)%mod)%mod;
			}
		}
		ans=(ans*q(n,mod-2))%mod;
		printf("%lld\n",ans);
    }    
    return 0;    
}  



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值