polya置换
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 #include<bits/stdc++.h> 2 #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-9 13 #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 , ls 17 #define rson mid + 1 , r , rs 18 #define ls (rt<<1) 19 #define rs (ls|1) 20 #define INF 0x3f3f3f3f 21 #define LINF 0x3f3f3f3f3f3f3f3f 22 #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*a 26 #define ll long long 27 #define ull unsigned long long 28 #define vi vector<int> 29 #define pii pair<int, int> 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 }