组合数学--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

View Code
 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

View Code
 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;

View Code
 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的方法,而且需要注意求逆元,由于模值为素数,故只需通过扩展欧几里德
    求出其逆元即可。  

View Code
  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.

 

posted @ 2012-02-16 15:39  hankers  阅读(866)  评论(0编辑  收藏  举报