polya计数

Let it Bead POJ 2409

题意:给定k种颜色给长度为n的项链着色,项链可以旋转和翻转,求非等价着色数
思路:Burnside定理,直接套用公式
N ( G , C ) = 1 ∣ G ∣ ∑ f ∈ G C ( f ) = 1 ∣ G ∣ ∑ f ∈ G k # ( f ) N(G,C)=\frac 1{|G|} \sum_{f\in G}C(f)=\frac 1{|G|}\sum_{f\in G}k^{\#(f)} N(G,C)=G1fGC(f)=G1fGk#(f)
分两种情况
(1)旋转:
假设我们旋转x个位置后与原来的着色方案相同,即第i个石头和第i+x个石头的颜色相同。递推下去得到第i个石头与第 i+xt 个石头颜色相同,所以 x t ≡ 0 ( m o d   n ) xt\equiv 0(mod\ n) xt0(mod n),求得最小的 t 是 n g c d ( x , n ) \frac n{gcd(x,n)} gcd(x,n)n t t t 就是一个循环因子的大小,所以有 n t \frac nt tn个循环因子,也就是gcd(x,n)个循环因子,每个循环因子有k种颜色可以选择,因此该情况下总的不变着色数是 ∑ C ( f ) = ∑ 0 ≤ i &lt; n k g c d ( i , n ) \sum C(f)=\sum_{0\le i&lt;n}k^{gcd(i,n)} C(f)=0i<nkgcd(i,n)

(2)翻转:
按对称轴讨论有多少种选择即可

  1. 如果n是奇数那么有:对于每一个对称置换 f f f有,顶点单独选择一种颜色,其余点按对称轴对应两点选择一种颜色,因此有 k × k n − 1 2 k\times k^{\frac {n-1}{2}} k×k2n1个着色保持不变,这样的置换一共有n种,因此总数为 n × k × k n − 1 2 n\times k\times k^{\frac {n-1}{2}} n×k×k2n1
  2. 如果n为偶数那么有:如果按顶点对称的,那么两个顶点选择一种颜色,其余点按对称轴对应两点选择一种颜色,有 k 2 × k n − 2 2 k^2\times k^{\frac {n-2}2} k2×k2n2,这样的置换有 n 2 \frac n2 2n个,因此有 n 2 × k 2 × k n − 2 2 \frac n2\times k^2\times k^{\frac {n-2}2} 2n×k2×k2n2
    如果按中线对称,那么就对应两点选择一种颜色,有 k n 2 k^{\frac n2} k2n个着色,这样的置换有 n 2 \frac n2 2n个,因此有 n 2 × k n 2 \frac n2\times k^{\frac n2} 2n×k2n

因此该情况下总的不变着色数为 n 2 × k n 2 + n 2 × k n 2 + 1 \frac n2\times k^{\frac n2}+\frac n2\times k^{\frac {n}2+1} 2n×k2n+2n×k2n+1

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define mp make_pair
#define ll long long
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define isZero(d)  (abs(d) < 1e-8)
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
const int mod=1e9+7;

ll n,k;

ll QuickPower(ll base,ll n)
{
	ll ret=1;
	while(n)
	{
		if(n&1)
			ret=ret*base;
		n>>=1;
		base=base*base;
	}
	return ret;
}

ll gcd(ll a,ll b)
{
	return b?gcd(b,a%b):a;	
}

int main()
{
	while(cin>>k>>n&&(n||k))
	{
		ll ans=0;
		for(int i=1;i<=n;++i)
			ans+=QuickPower(k,gcd(n,i));
		if(n&1)
			ans+=QuickPower(k,(n+1)/2)*n;
		else
			ans+=QuickPower(k,n/2+1)*n/2+QuickPower(k,n/2)*n/2;
		cout<<ans/(2*n)<<"\n";
	}
	return 0;
}

Invoker HDU - 3923

题意:用k个字母组成长度为n的单词,旋转翻转算同一种,问有多少种不同的单词
思路:和上题一样。唯一不同的是答案需要取余,最后除以2n的时候,需要算一下2n的逆元

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define mp make_pair
#define ll long long
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define isZero(d)  (abs(d) < 1e-8)
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
const int mod=1e9+7;

ll n,k;

ll QuickPower(ll base,ll n)
{
	ll ret=1;
	while(n)
	{
		if(n&1)
			ret=ret*base%mod;
		n>>=1;
		base=base*base%mod;
	}
	return ret;
}

ll gcd(ll a,ll b)
{
	return b?gcd(b,a%b):a;	
}

ll exgcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	ll q=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return q;
}

ll inverse(ll a,ll mod)
{
	ll x,y;
	exgcd(a,mod,x,y);
	return (x%mod+mod)%mod;
}

int main()
{
	int T;
	cin>>T;
	int Cas=0;
	while(T--)
	{
		cin>>k>>n;
		ll ans=0;
		for(int i=1;i<=n;++i)
			ans=(ans+QuickPower(k,gcd(n,i)))%mod;
		
		if(n&1)
			ans=(ans+QuickPower(k,(n+1)/2)*n)%mod;
		else
			ans=(ans+QuickPower(k,n/2+1)*n/2+QuickPower(k,n/2)*n/2)%mod;
		cout<<"Case #"<<++Cas<<": "<<ans*inverse(2*n,mod)%mod<<"\n";
	}
	return 0;
}

Color POJ - 2154

题意:用n种颜色给长度为n的项链着色,只能旋转,不能翻转,求非等价着色数
思路:只需要考虑旋转的情况,即
N ( G , C ) = 1 ∣ G ∣ ∑ f ∈ G k # ( f ) = 1 n ∑ 0 ≤ i &lt; n n g c d ( i , n ) N(G,C)=\frac 1{|G|}\sum_{f\in G}k^{\#(f)}=\frac 1n\sum_{0\le i &lt;n} n^{gcd(i,n)} N(G,C)=G1fGk#(f)=n10i<nngcd(i,n)
直接计算复杂度太高会超时,考虑合并最大公约数相同的项
N ( G , C ) = 1 n ∑ 0 ≤ i &lt; n n g c d ( i , n ) N(G,C)=\frac 1n\sum_{0\le i &lt;n} n^{gcd(i,n)} N(G,C)=n10i<nngcd(i,n)

= 1 n ∑ d ∣ n n d ∑ 0 ≤ i &lt; n [ g c d ( i , n ) = d ] =\frac 1n \sum_{d|n}n^d\sum_{0\le i&lt;n}[gcd(i,n)=d] =n1dnnd0i<n[gcd(i,n)=d]
= 1 n ∑ d ∣ n n d ∑ 0 ≤ i &lt; n [ g c d ( i d , n d ) = 1 ] =\frac 1n \sum_{d|n}n^d\sum_{0\le i&lt;n}[gcd(\frac id,\frac nd)=1] =n1dnnd0i<n[gcd(di,dn)=1]
= 1 n ∑ d ∣ n n d ∑ 0 ≤ t &lt; n d [ g c d ( t , n d ) = 1 ] =\frac 1n \sum_{d|n}n^d\sum_{0\le t&lt;\frac nd}[gcd(t,\frac nd)=1] =n1dnnd0t<dn[gcd(t,dn)=1]
= ∑ d ∣ n n d − 1 φ ( n d ) = \sum_{d|n}n^{d-1} \varphi(\frac nd) =dnnd1φ(dn)
因此我们枚举 n 的因子即可

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define mp make_pair
#define ll long long
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define isZero(d)  (abs(d) < 1e-8)
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
const int mod=1e9+7;

int T,N,P;

int visit[maxn],prime[maxn],cnt;
void Prime(int N)
{
	mes(visit,0);
	mes(prime,0);
	cnt=0;
	for(int i=2;i<N;++i)
	{
		if(!visit[i])
			prime[++cnt]=i;
		for(int j=1;j<=cnt&&i*prime[j]<N;++j)
		{
			visit[i*prime[j]]=1;
			if(i%prime[j]==0)
				break;
		}
	}
}

ll euler(ll n)
{
	ll ret=n,a=n;
	for(ll i=1;prime[i]*prime[i]<=a;++i)
	{
		if(a%prime[i]==0)
			ret=ret/prime[i]*(prime[i]-1);
		while(a%prime[i]==0)
			a/=prime[i];
	}
	if(a>1)
		ret=ret/a*(a-1);
	return ret;
}

ll QuickPower(ll base,ll n,ll mod=1e9+7)
{
	ll ret=1;
	while(n)
	{
		if(n&1)
			ret=(ret*base)%mod;
		n>>=1;
		base=(base*base)%mod; 
	}
	return ret;
}

int main()
{
	Prime(maxn);
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d %d",&N,&P); 
		ll ans=0;
		for(int i=1;i*i<=N;++i)
		{
			if(i*i==N)
				ans=(ans+QuickPower(N,i-1,P)*euler(i))%P;
			else if(N%i==0)
				ans=(ans+QuickPower(N,i-1,P)*euler(N/i)+QuickPower(N,N/i-1,P)*euler(i))%P;
		}
		printf("%lld\n",ans);
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值