POLYA定理的基本应用
题意:
有n个珠子围成的环,有t种颜色可以染这些珠子;如果这个环可以旋转有几种办法;如果这个环可以旋转,且可以翻转,有几种办法;刘汝佳的分析:
等价类计数问题。一共有两种置换,选择以及翻转。项链只有第一种置换,手镯则有两种置换。设所有珠子按逆时针编号0~n-1。
旋转置换:如果逆时针旋转i颗珠子的间距,则珠子0、i、2i、…构成一个循环。这个循环有n/gcd(i,n)个元素。根据对称性,所有循环的长度相同,因此一共有n/(n/gcd(i,n)) = gcd(i,n)个循环。该置换的不动点数为t^(gcd(i,n))。所有置换的不动点总数为a = sum{t^gcd(i,n) | i = 0,1,…,n - 1}。
翻转置换:分情况讨论。当n是奇数时,对称轴有n条,每条对称轴形成(n-1)/2个长为2的循环以及1个长为1的循环,即(n+1)/2个循环。这些置换的不动点总数是b=nt^((n+1)/2)。
当n是偶数时,有两种对称轴。穿过珠子的对称轴有n/2条,各形成n/2-1个长为2的循环,还形成两个长为1的循环;不穿过珠子的对称轴有n/2条,各形成n/2个长为2的循环。这些置换的不动点总数是b = n / 2 * (t ^ (n / 2 + 1) + t ^ (n / 2))。
根据Polya定理,项链总数为a/n,手镯总数是(a + b) / (2n)。
1 #include2 #define mp make_pair 3 #define pb push_back 4 #define first fi 5 #define second se 6 #define pw(x) (1ll << (x)) 7 #define sz(x) ((int)(x).size()) 8 #define all(x) (x).begin(),(x).end() 9 #define rep(i,l,r) for(int i=(l);i<(r);i++)10 #define per(i,r,l) for(int i=(r);i>=(l);i--)11 #define FOR(i,l,r) for(int i=(l);i<=(r);i++)12 #define eps 1e-913 #define PIE acos(-1)14 #define cl(a,b) memset(a,b,sizeof(a))15 #define fastio ios::sync_with_stdio(false);cin.tie(0);16 #define lson l , mid , ls17 #define rson mid + 1 , r , rs18 #define ls (rt<<1)19 #define rs (ls|1)20 #define INF 0x3f3f3f3f21 #define LINF 0x3f3f3f3f3f3f3f3f22 #define freopen freopen("in.txt","r",stdin);23 #define cfin ifstream cin("in.txt");24 #define lowbit(x) (x&(-x))25 #define sqr(a) a*a26 #define ll long long27 #define ull unsigned long long28 #define vi vector 29 #define pii pair 30 #define dd(x) cout << #x << " = " << (x) << ", "31 #define de(x) cout << #x << " = " << (x) << "\n"32 #define endl "\n"33 using namespace std;34 //**********************************35 int n,t;36 ll f[17];37 //**********************************38 void get(int n)39 {40 f[0]=1;41 FOR(i,1,n)f[i]=f[i-1]*t;42 }43 inline int gcd(int a,int b)44 {45 return b==0?a:gcd(b,a%b);46 }47 //**********************************48 int main()49 {50 while(~scanf("%d%d",&n,&t)){51 get(n);52 ll a=0,b=0;53 rep(i,0,n)a+=f[gcd(i,n)];54 if(n%2==1)b=n*(f[(n+1)/2]);55 else b=n/2*(f[n/2+1]+f[n/2]);56 printf("%lld %lld\n",a/n,(a+b)/2/n);57 }58 return 0;59 }