Polya定理通常用来解决置换群中的等价类计数问题;
设G = {a1,a2,a3,,,,,,am}是 N = {1,2,3,4,,,,n}上的一个置换群,现在用k中颜色对着n种对象进行染色,其不同的染色方案为:
ans = (k^c1 + k^c2 + k^c3 + ... k^cm) / m(ci为在置换ai的运算下循环的总数)
求解过程中关键是求ci的值。
Polya定理简单模板题目poj2409;
题目的的难点在于找每个置换的循环个数,其中,题目有两种置换:
一种是手镯旋转:旋转的情况下会有n中置换(隔0,1,2,,,,,n-1旋转各是一种置换),其中隔i颗珠子的间距在旋转过程,它的每个循环构成的元素个数为n/gcd(n,i)(因为n与i的
最小公倍数为n*i/gcd(n,i),用它除以每次旋转的间隔i,则为达到最小公倍数时,所需的旋转次数,即为循环中元素个数。),所以它的循环次数为gcd(n,i)(因为它的每次循环的
元素个数相同)
另一种是翻转,翻转比较好理解,分奇、偶和对称轴是否在珠子上讨论就好。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<climits>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<string>
#define ll long long
#define MAX 1000
#define eps 1e-8
#define INF INT_MAX
using namespace std;
ll gcd(ll a, ll b){
if (b == 0) return a;
gcd(b, a%b);
}
ll p[MAX];
int main(){
int c,n;
while (scanf("%d%d",&c,&n) && !(c == 0 && n == 0)){
p[0] = 1;
for (int i=1; i<=n; i++) p[i] = p[i-1]*c;
ll ans = 0;
for (int i=0; i<n; i++) ans += p[gcd(i,n)];
if (n % 2){
ans += n*p[(n+1)/2];
}
else{
ans += n/2*(p[n/2] + p[n/2+1]);
}
ans = ans/(2*n);
printf("%lld\n",ans);
}
return 0;
}
poj2154 polya定理,这个题对时间要求比较高,需要用欧拉定理优化,降低时间复杂度,再求欧拉函数的时候用到质因数分解,降低时间复杂度
#include<cstdio>
#include<cstring>
#include<cmath>
#include<climits>
#include<cstdlib>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#include<set>
#include<string>
#include<stack>
#define ll long long
#define MAX 36000
#define INF INT_MAX
#define eps 1e-8
using namespace std;
int P;
int mod_pow(int x, int n, int N)
{
int ret = 1;
x %= N;
while (n)
{
if (n & 1)
ret = ret * x % N;
x = x * x % N;
n >>= 1;
}
return ret;
}
int prime[MAX],mark[MAX],cnt;
void Prime(){
memset(mark,0,sizeof(mark));
cnt = 0;
for (int i=2; i< MAX; i++){
if (!mark[i]) prime[cnt++] = i;
for (int j=0; j<cnt && prime[j]*i <= MAX; j++){
mark[i*prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
}
int oula(int x)
{
int res=x;
for(int i=0; i<cnt&&prime[i]*prime[i]<=x; i++)
{
if(x%prime[i]==0)
{
res=res/prime[i]*(prime[i]-1);
while(x%prime[i]==0)
{
x/=prime[i];
}
}
}
if(x>1) res=res/x*(x-1);
return res % P; //此处要注意mod P,不然会WA,估计是因为下面调用的时候可能会爆int类型
}
int main(){
int T,n;
Prime();
scanf("%d",&T);
while (T--){
scanf("%d%d",&n,&P);
int i,ans = 0;
for (i = 1; i*i < n; i++){
if (n % i == 0){
int t1 = oula(i) * mod_pow(n,n/i-1,P) % P;
int t2 = oula(n/i) * mod_pow(n,i-1,P) % P;
ans = (ans + t1) % P;
ans = (ans + t2) % P;
//ans = (ans + mod_pow(n,i-1,P)*oula(n/i) + mod_pow(n,n/i-1,P)*oula(i)) % P;
}
}
if (i * i == n){
int t = oula(i) * mod_pow(n,i-1,P) % P;
ans = (ans + t) % P;
//ans = (ans + mod_pow(n,i-1,P)*oula(i)) % P;
}
printf("%d\n",ans % P);
}
return 0;
}
1805

被折叠的 条评论
为什么被折叠?



