polya的精髓就在与对循环节的寻找,其中常遇到的问题就是项链染色类问题。
当项链旋转时有n种置换,循环节的个数分别是gcd(n, i);
当项链翻转时有n种置换,其中当项链珠子数位奇数时,循环节的个数是n/2+1
当项链珠子数是偶数个时,当翻转线穿过珠子时,循环节个数为n/2+1,否则为n/2;
1.poj 1286:
题目大意:用三种颜色对珠子数不超过24的项链染色,问有多少种染色情况。
这道题是最基本的polya定理考察,只要带入公式即可
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; const long long maxa = 30; long long edge[maxa]; void rotate(long long n, long long ii){ for(long long i = 0; i < n; i++){ edge[(i+ii)%n] = i; } } void turn(long long n, long long ii){ for(long long i = n-1, j= 0; i >= 0; i--, j++){ edge[(j+ ii)%n] = i; } } long long vis[maxa]; void dfs(long long i){ if(vis[i] == 1)return ; vis[i] = 1; dfs(edge[i]); } int main(){ int n, m; while(scanf("%d%d", &m, &n), n+m){ if(n == 0){ printf("0 "); continue; } long long ans = 0; for(long long i = 0; i < n; i++){ rotate(n, i); long long o = 0; memset(vis, 0, sizeof(vis)); for(long long k = 0;k < n; k++){ if(vis[k]==0){ o ++; dfs(k); } } long long sum = 1; for(long long k = 0;k < o; k++){ sum *= m; } ans += sum; } for(long long i = 0; i < n; i++){ turn(n, i); long long o = 0; memset(vis, 0, sizeof(vis)); for(long long k = 0;k < n; k++){ if(vis[k]==0){ o ++; dfs(k); } } long long sum = 1; for(long long k = 0;k < o; k++){ sum *= m; } ans += sum; } cout<<ans/n/2<<endl; } }
2.poj 2409
题目大意,用c种颜色染n个珠子组成的项链,也是最基本的polya定理考察
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; const long long maxa = 30; long long edge[maxa]; void rotate(long long n, long long ii){ for(long long i = 0; i < n; i++){ edge[(i+ii)%n] = i; } } void turn(long long n, long long ii){ for(long long i = n-1, j= 0; i >= 0; i--, j++){ edge[(j+ ii)%n] = i; } } long long vis[maxa]; void dfs(long long i){ if(vis[i] == 1)return ; vis[i] = 1; dfs(edge[i]); } int main(){ int n, m; while(scanf("%d%d", &m, &n), n+m){ if(n == 0){ printf("0 "); continue; } long long ans = 0; for(long long i = 0; i < n; i++){ rotate(n, i); long long o = 0; memset(vis, 0, sizeof(vis)); for(long long k = 0;k < n; k++){ if(vis[k]==0){ o ++; dfs(k); } } long long sum = 1; for(long long k = 0;k < o; k++){ sum *= m; } ans += sum; } for(long long i = 0; i < n; i++){ turn(n, i); long long o = 0; memset(vis, 0, sizeof(vis)); for(long long k = 0;k < n; k++){ if(vis[k]==0){ o ++; dfs(k); } } long long sum = 1; for(long long k = 0;k < o; k++){ sum *= m; } ans += sum; } cout<<ans/n/2<<endl; } }
3.hdu 1812
题目大意,用c种颜色对n*n的棋盘染色,有多少种方法
其实这道题就是可以看成一些(n+1)/2串项链,有八种置换,但是最坑的是大数怎么写都超时....万进制优化也超时.....打表还不让大那么大,java是很好的解决方法...谁爱写谁写....反正我不写啦啦啦啦
4.poj 2154
题目大意,用n种颜色染长度是n的项链,结果mod p n<=10^9,p<= 30000,这里的项链只考虑旋转。
常规解法是这样sum(n^gcd(n,i))%q,但是数据太大了不允许,我们知道的是n的因子数是可以通过搜索求出来的,那么如果gcd(n,i) == m,则i的数量显然是euler(i),需要注意的是最后除n的时候,n和p不一定互质所以需要在之前做处理,也就是将公式变成了sum(n^gcd(n,i)-1)%q,需要注意的是开long long会超时,所以在要用int,有些部分需要用同余模定理优化。
#include<iostream> #include<string.h> #include<cmath> #include<stdio.h> using namespace std; const int maxa = 50; int app[maxa][2]; int o; int ans; int n, q; int m; int euler(int n){ //返回euler(n) //if(n == 1)return 0; int res=n,a=n; for(int i=2;i*i<=a;i++){ if(a%i==0){ res=res/i*(i-1);//先进行除法是为了防止中间数据的溢出 while(a%i==0) a/=i; } } if(a>1) res=res/a*(a-1); return res; } int kuick(int a, int b){ int aa = 1; if(b == 0)return 1; aa = kuick(a, b/2); aa *= aa; aa%= q; if(b &1) aa*= a; aa %= q; return aa; } void dfs(int ii, int nn){//cout<<"*"<<ii<<" "<<o<<" "<<nn<<endl; if(ii == o){ int aa = euler(n/nn) % q; ans += aa * kuick(n%q, nn-1); ans %= q; return; } dfs(ii+1, nn); int ss = 1; for(int i = 0; i < app[ii][1]; i++){ ss *= app[ii][0]; dfs(ii+1, ss*nn); } } int main(){ cin>>m; while(m--){ cin>>n>>q; int N = n; ans = 0; int sq = sqrt(n); o = 0; for(int i = 2;i <= sq; i++){ if(n % i == 0){ app[o][0] = i; app[o][1] = 0; while(n %i ==0){ app[o][1]++; n /= i; } o++; } } //printf("*"); if(n != 1){ app[o][0] = n; app[o++][1] = 1; } n = N; dfs(0, 1); cout<<ans<<endl; } }