题意:用c种颜色给长度为s的项链染色,一共有多少种方案
思路:网选后天就来了,这两天补了下Polya定理,刚好又在学习密码学,顺便补了群的一些东西。对于这种存在置换后存在一个等价类的计数问题,我们可以用Burnside引和Polya定理,Polya定理是由Burnside定理推导出来的,学习过程应该是群->置换群->Burnside引理->Polya定理,同时可以看见的是尽管Polya定理是由Burnside引理推导出来的,但也可以看作是两个看问题不同的角度而得出的结论,Burnside引理着眼于染色,如果颜色种类过多和被染色的图形过于复杂,那么Burnside引理将很难操作,而Polya定理则着眼于图形的结构,使问题的复杂性大大降低,数学真是处处充满哲理,不同角度看问题可能会产生非常大的差别
按照顺时针方向从0到n - 1进行编号,Polya定理是对结构进行置换,只考虑旋转的话,有n种结构,分别是旋转0, 1, 2, ... , n - 1,假定现在旋转了k次,
那么第i个钻石和第(i + k * t)属于同一个循环,那么如果求出一个最小的t使得(i + k * t ) % n == i,那么此时表明这个循环就有t个元素,上述式子可以推出i % n + k * t % n == i,可推出(k * t) % n == 0,此时t要取最小,必然t = n / gcd(k, n):t表示一个循环的阶,那么n / t就表示有多少个循环,从对称性上讲,也可看出是正确的。
当然还要考虑翻转的情况
题目链接:http://poj.org/problem?id=2409
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b); }
ll mypow(ll x, ll n)
{
ll res = 1;
while(n)
{
if(n & 1) res *= x;
x *= x;
n >>= 1;
}
return res;
}
ll c, s;
ll polya()
{
ll ans = 0;
for(ll i = 1; i <= s; i++)
ans += mypow(c, gcd(i, s));
if(s & 1)
ans += s * mypow(c, (s + 1) / 2);
else
ans += (s / 2) * (mypow(c, s / 2 + 1) + mypow(c, s / 2));
return ans / 2 / s;
}
int main()
{
while(scanf("%lld%lld", &c, &s), c)
{
ll ans = polya();
printf("%lld\n", ans);
}
return 0;
}