组合数学--Polya原理
组合数学--Polya定理
类型题总结:
POJ 1286 Necklace of Beads
POJ 2409 Let it Bead
POJ 2154 Color
POJ 2888 Magic Bracelet
HDU 1812 Count the Tetris
Polya定理:设G是n个对象的一个置换群,用m种颜色涂染这n个对象,则不同染色的方案数L=1/|G|*[m^p(a1)+m^p(a2)+....+m^p(an)].其中p(ai)是某个置换的循环数.
1.旋转置换.
我们假设依次顺时针旋转1~n个,则循环个数=gcd(i,n);
2.翻转置换
当n为偶数时,分两种情况,一种是中心轴在两个对称对象上,则循环个数为n/2+1,另一种是对称轴两边分别有n/2个对象,则循环个数为n/2;
当n为奇数时,对称轴就只能在一个对象上,则循环个数为n/2+1;
至于该原理的推导过程我还是不太懂,没有完全掌握,争取明天把自己对该定理的具体推导过程写出来,和大家分享吧
代码:
1.poj 1286
1 #include<iostream> 2 #include<math.h> 3 using namespace std; 4 5 int gcd(int a,int b) 6 { 7 return b?gcd(b,a%b):a; 8 } 9 int main() 10 { 11 int n,i; 12 double sum; 13 while(cin>>n&&n!=-1) 14 { 15 sum=0; 16 if(n==0) 17 { 18 printf("0\n"); 19 continue; 20 } 21 for(i=1;i<=n;i++) 22 { 23 sum+=pow(3.0,gcd(n,i)); 24 } 25 if(n%2==0) 26 sum+=(pow(3.0,n/2)+pow(3.0,n/2+1))*n/2; 27 else 28 sum+=pow(3.0,n/2+1)*n; 29 sum=sum/2/n; 30 cout<<(int)sum<<endl; 31 } 32 return 0; 33 }
2.poj 1812
边长为n的正方形,c中颜色。
旋转只有 0,90,180,270度三种旋法。
旋0度,则置换的轮换数为n*n
旋90度,n为偶数时,则置换的轮换数为n*n/4,n为奇数,则置换的轮换数为(n*n-1)/4+1
旋180度,n为偶数时,则置换的轮换数为n*n/2,n为奇数,则置换的轮换数为(n*n-1)/2+1
旋270度,n为偶数时,则置换的轮换数为n*n/4,n为奇数,则置换的轮换数为(n*n-1)/4+1
反射 沿对角反射两种,沿对边中点连线反射两种
n为偶数时,沿对边中点连线反射两种的置换轮换数为 n*n/2
沿对角反射两种的置换轮换数为 (n*n-n)/2+n
n为奇数时,沿对边中点连线反射两种的置换轮换数为 (n*n-n)/2+n
沿对角反射两种的置换轮换数为 (n*n-n)/2+n
综上所述:用m种颜色给n*n矩形染色的方案数L
S=m^(n*n)+m^((n*n+3)/4)+m^((n*n+1)/2)+m^((n*n+3)/4) (考虑旋转时)
n为奇数L= (S+4*m^((n*n+n)/2)) / 8
n为偶数L= (S+2*m^((n*n+n)/2)+2*m(n*n/2) )/ 8
1 import java.io.*; 2 import java.util.*; 3 import java.math.*; 4 5 6 public class Main 7 { 8 public static void main(String args[]) throws Exception 9 { 10 Scanner cin=new Scanner(System.in); 11 int n,s1,s2,s; 12 BigInteger c; 13 BigInteger two = new BigInteger("2"); 14 BigInteger eight = new BigInteger("8"); 15 BigInteger four = new BigInteger("4"); 16 BigInteger sum; 17 while(cin.hasNext()) 18 { 19 n=cin.nextInt(); 20 c=cin.nextBigInteger(); 21 if(n%2!=0) 22 { 23 s1=(n*n-1)/4+1; 24 s2=(n*n-1)/2+1; 25 } 26 else 27 { 28 s1=n*n/4; 29 s2=n*n/2; 30 } 31 32 sum=c.pow(s2); 33 sum=sum.add(c.pow(s1).multiply(two)); 34 sum=sum.add(c.pow(n*n)); 35 if(n%2!=0) 36 { 37 s=(n*n+n)/2; 38 sum=sum.add(c.pow(s).multiply(four)); 39 } 40 else 41 { 42 s=(n*n+n)/2; 43 sum=sum.add(c.pow(s2).multiply(two)); 44 sum=sum.add(c.pow(s).multiply(two)); 45 } 46 sum=sum.divide(eight); 47 System.out.println(sum.toString()); 48 } 49 } 50 }
3.poj 2154
题意:给n个珠子,n种颜色,经旋转可得到的为同一种。
首先想到旋转,循环个数为gcd(i,n),(1=<i<=n),则每个循环节的长度L=n/gcd(i,n),但如果直接枚举i的话,
时间复杂度过高,必然TLE,所以考虑枚举L,所以对于0=<i<=n-1中 找出gcd(i,n)==n/L有多少。
设a=n/L=gcd(i,n),i=a*t,则gcd(a*L,a*t)=a;所以会有Eluer(L)个t满足gcd(a*t,n)==n/L;
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 6 using namespace std; 7 const int MAX=100005; 8 typedef long long ll; 9 10 int prime[MAX],mark[MAX]; 11 12 void Prime() 13 { 14 int p=0; 15 for(int i=1;i<MAX;i++) 16 mark[i]=i; 17 for(int i=2;i<MAX;i++) 18 { 19 if(mark[i]==i) 20 prime[p++]=i; 21 for(int j=0;j<p&&prime[j]*i<MAX;j++) 22 { 23 mark[prime[j]*i]=prime[j]; 24 if(i%prime[j]==0) 25 break; 26 } 27 } 28 } 29 ll Eluer(int n) 30 { 31 ll eluer=n; 32 for(int i=0;prime[i]*prime[i]<=n;i++) 33 { 34 35 if(n%prime[i]==0) 36 { 37 eluer=eluer*(prime[i]-1)/prime[i]; 38 while(n%prime[i]==0) 39 { 40 n/=prime[i]; 41 } 42 } 43 } 44 if(n!=1) 45 eluer=eluer*(n-1)/n; 46 return eluer; 47 } 48 49 50 ll Pow(int n,int m,int mod) 51 { 52 ll a=1,b=n; 53 while(m) 54 { 55 if(m%2) 56 { 57 a*=b; 58 a%=mod; 59 } 60 b*=b; 61 b%=mod; 62 m/=2; 63 } 64 return a; 65 } 66 int main() 67 { 68 int t,i,n,mod; 69 ll sum; 70 Prime(); 71 scanf("%d",&t); 72 while(t--) 73 { 74 sum=0; 75 scanf("%d%d",&n,&mod); 76 for(i=1;i*i<n;i++) 77 { 78 // printf("i=%d\n",i); 79 if(n%i==0) 80 { 81 sum+=Eluer(i)%mod*Pow(n,n/i-1,mod); 82 sum=sum%mod; 83 sum+=Eluer(n/i)%mod*Pow(n,i-1,mod); 84 sum=sum%mod; 85 } 86 87 } 88 if(i*i==n) 89 { 90 sum+=Eluer(i)%mod*Pow(n,i-1,mod); 91 sum%mod; 92 } 93 printf("%lld\n",sum); 94 } 95 return 0; 96 }
4.hdu 3923
这题是poj 1286的升级版本,但用到了poj 2154的方法,而且需要注意求逆元,由于模值为素数,故只需通过扩展欧几里德
求出其逆元即可。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 using namespace std; 6 typedef long long ll; 7 const ll MOD=1000000007; 8 const int MAX=10005; 9 int eluer[MAX]; 10 11 ll Pow(int n,int m) 12 { 13 ll a=1,b=n; 14 while(m) 15 { 16 if(m%2) 17 { 18 a*=b; 19 a%=MOD; 20 } 21 b*=b; 22 b%=MOD; 23 m/=2; 24 } 25 return a; 26 } 27 28 void Eluer() 29 { 30 memset(eluer,0,sizeof(eluer)); 31 eluer[1]=1; 32 for(int i=2;i<MAX;i++) 33 { 34 if(!eluer[i]) 35 { 36 for(int j=i;j<MAX;j+=i) 37 { 38 if(!eluer[j]) 39 eluer[j]=j; 40 eluer[j]=eluer[j]*(i-1)/i; 41 } 42 } 43 } 44 } 45 46 ll Extgcd(ll a,ll b,ll &x,ll &y) 47 { 48 if(b==0) 49 { 50 x=1;y=0;return a; 51 } 52 ll d=Extgcd(b,a%b,x,y); 53 ll t=x;x=y;y=t-a/b*y; 54 return d; 55 } 56 57 ll GetN(ll ans,int n) 58 { 59 ll x,y; 60 Extgcd((ll)n,MOD,x,y); 61 ans=ans*x%MOD; 62 if(ans<0) 63 ans+=MOD; 64 return ans; 65 } 66 ll polya(int m,int n) 67 { 68 int i; 69 ll sum=0; 70 for(i=1;i*i<n;i++) 71 { 72 if(n%i==0) 73 { 74 sum=(sum+eluer[i]*Pow(m,n/i))%MOD; 75 sum=(sum+eluer[n/i]*Pow(m,i))%MOD; 76 } 77 } 78 if(i*i==n) 79 sum=(sum+eluer[i]*Pow(m,i))%MOD; 80 if(n%2==0) 81 sum=(sum+((Pow(m,n/2)+Pow(m,n/2+1))*n/2))%MOD; 82 else 83 sum=(sum+(Pow(m,n/2+1)*n))%MOD; 84 sum=GetN(sum,2*n); 85 return sum; 86 } 87 int main() 88 { 89 int n,i,a,b; 90 ll sum; 91 Eluer(); 92 while(scanf("%d",&n)!=EOF) 93 { 94 for(i=1;i<=n;i++) 95 { 96 scanf("%d%d",&a,&b); 97 sum=polya(a,b); 98 printf("Case #%d: ",i); 99 printf("%lld\n",sum); 100 } 101 } 102 return 0; 103 }
5.