题目大意:
用红蓝绿三色共n个珠子串成项链,求旋转和翻转后仍不一样的串法种数?
解题思路:
首先说一下polya定理:
设G是n个对象的一个置换群 { p1,p2……,pk } (即所有的变换方式),用m种颜色对这n个对象染色,则不同的方案数为
l=1|G|(mc(p1)+mc(p2)+……+mc(pk))
其中c(pi)表示置换pi的循环节个数
那么再说这道题:
对应题目的旋转问题,直接套用现成结论。
一共n个置换,第i个置换为顺(逆)时针旋转i个珠子,其循环节的个数为gcd(n, i)。
对应题目的翻转问题,分奇偶讨论。
奇数时,有一种置换。
对称轴是一个珠子到圆心的连线,一共n条。选定对称轴后,对称轴上的一个珠子不动,构成一个循环,其他n-1个分别以对称轴对称的珠子两两交换,构成(n-1)/2个循环,所以循环节的个数是 (n + 1) / 2 。
偶数时,有两种置换。
(1)对称轴可能是两个珠子的连线,一共 n / 2条。选定对称轴后,对称轴上的两个珠子不动,构成两个循环,其他n-2个分别以对称轴对称的珠子两两交换,构成(n-2)/2个循环;
(2)对称轴还可能是两个珠子的中点和圆心的连线,所有珠子两两对称的交换,构成n / 2 个循环。
讨论完毕,直接套用polya定理即可。
注意题目中有n=0的情况要特判,不然不能求gcd。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<queue>
#include<vector>
#include<set>
#include<map>
#define ll long long
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
if(c=='-')c=getchar(),f=-1;
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
const int N=25;
ll Pow[N];
int main()
{
//freopen("lx.in","r",stdin);
Pow[0]=1;
for(int i=1;i<N;i++)Pow[i]=3ll*Pow[i-1];
int n;
while(1)
{
n=getint();
if(n==-1)break;
if(!n)
{
puts("0");
continue;
}
ll ans=0;
for(int i=1;i<=n;i++)
ans+=Pow[gcd(i,n)];
if(n&1)ans+=1ll*n*Pow[(n+1)/2];
else
{
ans+=1ll*(n/2)*Pow[(n+2)/2];
ans+=1ll*(n/2)*Pow[n/2];
}
ans/=2*n;
cout<<ans<<'\n';
}
return 0;
}