[POJ2888] Magic Bracelet

[POJ2888] Magic Bracelet

题目描述

简要题意:给圆上n个点染色,颜色有m种,其中k对颜色不能相邻,循环同构,多组数据,询问染色方案数。

 

Solution

大概就是一道挺显然的Burnside题(一般染色,循环同构都酱紫做)。

对于一个圆上的循环同构,显然有n个置换,用Burnside引理求解答案:

|G||X/G|=\sum_{g\in G}|X^{g}|

所以我们可以枚举置换中的循环个数x,令  solve(x)  为不考虑循环同构时,x个点m种颜色,满足所有限制的方案数,最后

Ans=\sum solve(i)*\phi(\frac{n}{i})

现在的问题转化为,如何求出  solve(x),这是一个经典问题,可以DP解决。

f[i][j] 为线段上i个点,第i个点的颜色为j,满足其他所有限制的方案数。

f[i][j]+=f[i-1][k]\;\;\;\;\;\;\;\;\;\;(flag[j][k]=1) 

这样的DP是  O(nm^2),直接矩阵乘法优化成  O(m^3\lg n) 。

最后的时间复杂度大概是——  O(\sqrt{n}\;m^3\lg n)。(实测需要卡常,实际速度和POJ测评机有关)

#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <string.h>
//#include <unordered_set>
//#include <unordered_map>
//#include <bits/stdc++.h>

#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se second

using namespace std;

template<typename T>inline bool upmin(T &x,T y) { return y<x?x=y,1:0; }
template<typename T>inline bool upmax(T &x,T y) { return x<y?x=y,1:0; }

typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int,int> PR;
typedef vector<int> VI;

const lod eps=1e-11;
const lod pi=acos(-1);
const int oo=1<<30;
const ll loo=1ll<<62;
const int mods=9973;
const int MAXN=1000005;
const int INF=0x3f3f3f3f;//1061109567
/*--------------------------------------------------------------------*/
inline int read()
{
	int f=1,x=0; char c=getchar();
	while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
	while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
	return x*f;
}
int flag[MAXN],phi[MAXN],prime[MAXN],pnum=0,n,m,k;
int Phi(int x)
{
	int cnt=x;
	if (x<=1e6) return phi[x];
	for (int i=1;prime[i]*prime[i]<=x;i++)
	if (!(x%prime[i]))
	{
		cnt=cnt-cnt/prime[i];
		while (!(x%prime[i])) x/=prime[i];
	}
	return (x>1)?cnt-cnt/x:cnt;
}
void Init_Prime(int n)
{
	flag[1]=phi[1]=1; 
	for (int i=2;i<=n;i++)
	{
		if (!flag[i]) prime[++pnum]=i,phi[i]=i-1;
		for (int j=1;j<=pnum&&prime[j]*i<=n;j++)
		{
			flag[prime[j]*i]=1;
			if (!(i%prime[j])) { phi[prime[j]*i]=phi[i]*prime[j]; break; }
			phi[prime[j]*i]=phi[i]*phi[prime[j]];
		}
	}
}
int upd(int x,int y){ return x+y>=mods?x+y-mods:x+y; }
struct Matrix 
{
    int n,A[10][10];
    Matrix(int n1=0) 
    {
        n=n1;
        memset(A,0,sizeof A); 
    }
    void Init() { memset(A,0,sizeof A); }
    void init() { for(int i=0;i<n;i++) A[i][i]=1; }
    Matrix operator * (const Matrix &b)
    {
        Matrix ans(n);
        for(int i=0;i<n;i++) 
            for(int j=0;j<n;j++) 
                for(int k=0;k<n;k++) ans.A[i][k]=upd(ans.A[i][k],A[i][j]*b.A[j][k]%mods);
        return ans;
    }
    Matrix operator ^ (const ll &b)  
    {
        Matrix ret(n),x=*this; 
        ret.init();
        for (ll y=b;y;y>>=1) 
        {
            if (y&1) ret=ret*x;
            x=x*x;
        }
        return ret;
    }
    void print()
    {
    	for (int i=0;i<n;i++)
    	{
    		for (int j=0;j<n;j++) cout<<setw(4)<<A[i][j];
    		cout<<endl;
    	}
    	cout<<endl;
	}
} f,g;
int getans(int x)
{
	g=f^x;
	ll ans=0;
	for (int i=0;i<m;i++) ans+=g.A[i][i];
	return ans;
}
int solve(int x,int y)
{
	if (y==0) return 1;
	int q=solve(x,y>>1);
	return (y&1)?1ll*q*q%mods*x%mods:1ll*q*q%mods;
}
int main()
{
	Init_Prime(1000000);
	cout<<Phi(1000000000)<<endl;
	int Case=read();
	while (Case--)
	{
		n=read(),m=read(),k=read();
		f.n=m;
		for (int i=0;i<m;i++)
			for (int j=0;j<m;j++) f.A[i][j]=1;
		for (int i=1;i<=k;i++) 
		{
			int x=read()-1,y=read()-1;
			f.A[x][y]=0;
			f.A[y][x]=0;
		}
		ll ans=0;
		for (int i=1;i*i<=n;i++)
		if (n%i==0)
		{
			ans=upd(ans,1ll*Phi(n/i)*getans(i)%mods);
			if (i*i!=n) ans=upd(ans,1ll*Phi(i)*getans(n/i)%mods);
//			cout<<ans<<endl;
		}
		printf("%d\n",1ll*ans*solve(n,mods-2)%mods);
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值