(1)有一种置换是旋转,将项链顺时针旋转i格后,其循环节数位gcd(n,i)
(2)通过翻转的情况
1.当n为奇数时,共有n个循环节数(n+1)/2的循环群
2.当n为偶数时,共有n/2个循环节数为(n+2)/2的循环群,和n/2个循环节数为n/2的循环群
#include<iostream>
#include<cmath>
using namespace std;
typedef long long ll;
ll pow(int value,int num)
{
ll sum=1;
for(int i=1;i<=num;i++){
sum*=value;
}
return sum;
}
int gcd(int n,int m)
{
return m==0?n:gcd(m,n%m);
}
ll polya(int color,int num)
{
ll sum=0;
for(int i=1;i<=num;i++){
sum+=pow(color,gcd(num,i));
}
if(num&1){
sum+=num*pow(color,(num+1)/2);
}else{
sum+=(pow(color,num/2+1)+pow(color,num/2))*num/2;
}
return sum/2/num;
}
int main()
{
int color,num;
while(scanf("%d%d",&color,&num)&&color&&num){
printf("%lld\n",polya(color,num));
}
return 0;
}
另一种自己构造置换,然后直接算出循环节,这种写出来会超时,但是思想绝对值得一提
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=1000000;
int color,num,perm[maxn],visit[maxn];
ll pow(int value,int num)
{
ll sum=1;
for(int i=1;i<=num;i++){
sum*=value;
}
return sum;
}
int cycle()
{
ll pos,total=0;
memset(visit,0,sizeof(visit));
for(int i=0;i<num;i++){
if(!visit[i]){
pos=i;
total++;
for(int j=0;!visit[perm[pos]];j++){
pos=perm[pos];
visit[pos]=1;
}
}
}
return total;
}
ll polya(int color,int num)
{
ll sum=0;
for(int i=0;i<num;i++){
for(int j=0;j<num;j++){
perm[j]=(i-j+num)%num;
}
sum+=pow(color,cycle());
}
if(num&1){
for(int i=0;i<num;i++){
for(int j=0;j<num;j++){
perm[(i+j)%num]=(i-j+num)%num;
}
sum+=pow(color,cycle());
}
}else{
for(int i=0;i<num/2;i++){
for(int j=0;j<num;j++){
perm[(i+j)%num]=(i-j+num)%num;
}
sum+=pow(color,cycle());
}
for(int i=0;i<num/2;i++){
for(int j=0;j<num;j++){
perm[(i-j+num)%num]=(i+j+1)%num;
}
sum+=pow(color,cycle());
}
}
return sum/2/num;
}
int main()
{
while(scanf("%d%d",&color,&num)&&color&&num){
printf("%lld\n",polya(color,num));
}
return 0;
}