Polya计数

hdu4633

题目:用k种颜色给一个魔方染色,可以染每个面的9个小矩形,12条棱,8个顶点(总之就是有74个能染的地方),空间旋转后一样的视为相同,问有多少种不同的染色方案。

思路:裸的polya计数,但是这个立方体的对称群本来就很容易弄错。。。《组合数学》里有个例题提到立方体的对称群有24个元素,分别是:

(1)恒等变换。

(2)以两个相对面的中心相连作为对称轴,旋转(i)90,(ii)180,(iii)270度,每种有3个。共9种。

(3)以两个相对棱的中点连线为对称轴翻转180度,有6种。

(4)固定两个相对顶点旋转(i)120,(ii)240度,共8种。

Burnside引理指出,在一个对称变换群作用下不同的染色数目等于每种变换作用下不动点个数的平均数。

但是此题直接求不动点恐怕只能写暴力程序跑了。。。polya定理实际上是Burnside引理稍微一般化了一下。polya定理提出了一种计算不动点数目的较为简便的方法,就是计算在每种映射下,映射中“环”的数目。这个可以通过列表比较清楚地得出。(polya定理还给出了更一般的通过母函数法求解限制每种颜色涂色的数目的方法,但是此题不需要)

那么我们可以得出下表:

                   面                              棱                              顶点

(2) (i) (iii)  (2,0,0,13)          (0,0,0,3)                          (0,0,0,2)

(2) (ii)       (2,26,0,0)          (0,6,0,0)                          (0,4,0,0)

(3)            (0,27,0,0)          (2,5,0,0)                          (0,4,0,0)

(4)(i)(ii)     (0,0,18,0)          (0,0,4,0)                          (2,0,2,0)

(列表方法见《组合数学》Brualdi)

然后就是一发公式交上去就能AC了

/*
* @author:  Cwind
*/

///#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>

using namespace std;
#define pb push_back
#define PB pop_back
#define bk back()
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps (1e-10)
#define INF (1000000300)
#define clr(x) memset((x),0,sizeof (x))
#define cp(a,b) memcpy((a),(b),sizeof (b))

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;

const ll mod=10007;
ll qpow(ll a,ll p){
    ll ans=1;
    while(p){
        if(p&1) ans=ans*a%mod;
        a=a*a%mod;
        p>>=1;
    }
    return ans;
}
int T,K;
int inv=qpow(24,mod-2);
int cas;
int main(){
    freopen("/home/slyfc/CppFiles/in","r",stdin);
    cin>>T;
    while(T--){
        scanf("%d",&K);
        ll ans=qpow(K,74)+6*qpow(K,20)+9*qpow(K,38)+8*qpow(K,26);
        ans%=mod;
        ans*=inv;
        ans%=mod;
        printf("Case %d: %lld\n",++cas,ans);
    }
    return 0;
}
View Code

 poj 1286

题目:用k种颜色给一串珠子染色,绕中心旋转和翻转后相同的算一种,问有多少种不同的染色方法。

思路:比较好办的是对称翻转的情况,奇数和偶数略有不同但是都很好算。旋转的情况比较麻烦,包括不转的映射(I)一共有n种旋转。对于旋转k个的旋转操作,设旋转t次发生重复,那么k*t==0(mod n),可见t=n/gcd(n,k)。所以这类珠子有n/t=gcd(n,k)种。那么这个题的数据做到这里就可以暴力枚举了(感觉上只有23的n手算也不是不可以。。。)。但是《挑战程序设计竞赛》里给了一种效率更高的算法,所以我就写了一下。主要是统计上,因为gcd(n,k)的取值只能是n的约数,所以可以统计每个约数对总和的贡献。又有在小于n的数中gcd(k,n)==d的数的个数等于phi(n/d)。然后就可以分解n,然后算旋转的贡献了。

/*
* @author:  Cwind
*/

///#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>

using namespace std;
#define pb push_back
#define PB pop_back
#define bk back()
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps (1e-10)
#define INF (1000000300)
#define clr(x) memset((x),0,sizeof (x))
#define cp(a,b) memcpy((a),(b),sizeof (b))

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;

vector<ll> dvs,pdvs;
ll eular[100];
void fac(ll n){
    dvs.clear();pdvs.clear();
    for(ll d=2;d*d<=n;d++){
        if(n%d==0){
            dvs.pb(d);
            if(d*d!=n) dvs.pb(n/d);
        }
    }
    dvs.pb(1);
    ll d=2;
    while(d*d<=n){
        if(n%d==0){
            pdvs.pb(d);
            while(n%d==0){
                n/=d;
            }
        }
        d++;
    }
    if(n>1) pdvs.pb(n);
}
int n;
void getEular(){
    for(int i=0;i<dvs.size();i++){
        ll d=n/dvs[i];
        ll eu=d;
        for(int j=0;j<pdvs.size();j++){
            ll p=pdvs[j];
            if(d%p==0) eu/=p,eu*=p-1;
        }
        eular[i]=eu;
    }
}
ll qpow(ll a,ll p){
    ll ans=1;
    while(p>0){
        if(p&1) ans=ans*a;
        p>>=1;
        a=a*a;
    }
    return ans;
}
int main(){
    freopen("/home/slyfc/CppFiles/in","r",stdin);
    while(scanf("%d",&n),n!=-1){
        if(n==0){
            puts("0");
            continue;
        }
        if(n==1){
            puts("3");
            continue;
        }
        fac(n);
        getEular();
        ll ans=qpow(3,n);
        for(int i=0;i<dvs.size();i++){
            ans+=eular[i]*qpow(3,dvs[i]);
        }
        if(n%2==1){
            ans+=n*qpow(3,(n+1)/2);
        }else{
            ans+=n/2*qpow(3,n/2)+n/2*qpow(3,n/2+1);
        }
        ans/=n*2;
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

 HDU 5080

题目:有若干个点,某些点间有边.给点染色,旋转后相同的视为一种,问染色方案数.

思路:首先旋转中心必然是所有点的坐标取均值的那个点,然后暴力旋转n种转法,如果图形重合,就是一个置换群,找出方案数加到答案里......先把点按极角排序,如果旋转后相应的点坐标相同,并且相应的点间是否有边的情况也一样,那这个旋转就是与原图形重合的.

/*
* @author:  Cwind
*/
#include <bits/stdc++.h>
using namespace std;
#define IOS std::ios::sync_with_stdio (false);std::cin.tie(0)
#define pb push_back
#define PB pop_back
#define bk back()
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps (1e-4)
#define INF (1000000300)
#define clr(x) memset((x),0,sizeof (x))
#define cp(a,b) memcpy((a),(b),sizeof (b))

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;


const double EPS=1e-8;
const int maxn=60;
int sgn(double x){if(fabs(x)<EPS) return 0;else return x<0?-1:1;}
struct Point{
    double x,y;
    double ang;
    Point(double x=0,double y=0):x(x),y(y){}
    Point operator + (const Point &C)const{return Point(x+C.x,y+C.y);}
    Point operator - (const Point &C)const{return Point(x-C.x,y-C.y);}
    Point operator / (const double &p)const{return Point(x/p,y/p);}
    bool operator == (const Point &C)const{return sgn(x-C.x)==0&&sgn(y-C.y)==0;}
    Point rotate(const Point &p,double ang){
        Point A=(*this)-p;
        double c=cos(ang),s=sin(ang);
        return Point(p.x+A.x*c-A.y*s,p.y+A.x*s+A.y*c);
    }
    double dis(const Point &C)const{return sqrt(sq(x-C.x)+sq(y-C.y));}
};
Point p[maxn],center;
bool cmp(int x,int y){
    Point a=p[x],b=p[y];
    if(sgn(a.ang-b.ang)!=0) return a.ang<b.ang; 
    else return a.dis(center)<b.dis(center);
}
const ll mod=1e9+7;
ll qpow(ll a,ll p){
    ll ans=1;
    while(p){
        if(p&1) ans=(ans*a)%mod;
        a=(a*a)%mod;p>>=1;
    }
    return ans;
}
ll inv(ll a){return qpow(a,mod-2);}
int T,n,m,c;
int G[maxn][maxn];
int link[maxn];
vector<int> V;
Point tmp[maxn];
int sz;
bool check(){
    double a=p[link[V[0]]].ang-p[V[0]].ang;
    for(int i=0;i<sz;i++){
        tmp[i]=p[V[i]].rotate(center,a);
    }
    for(int i=0;i<sz;i++){
        if(!(p[link[V[i]]]==tmp[i])){
            return 0;
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(G[i][j]!=G[link[i]][link[j]]){
                return 0;
            }
        }
    }
    return 1;
}
bool vis[maxn];
ll cal(){
    clr(vis);
    ll cnt=0;
    for(int i=1;i<=n;i++){
        if(!vis[i]){
            cnt++;
            vis[i]=1;
            int j=link[i];
            while(!vis[j]){
                vis[j]=1,j=link[j];
            }
        }
    }
    return cnt;
}
int main(){
    freopen("/home/slyfc/CppFiles/in","r",stdin);
    //freopen("/home/slyfc/CppFiles/out","w",stdout);
    scanf("%d",&T);
    while(T--){
        center.x=center.y=0;
        scanf("%d%d%d",&n,&m,&c);
        for(int i=1;i<=n;i++){
            scanf("%lf%lf",&p[i].x,&p[i].y);
            center=center+p[i];
        }
        center=center/n;
        clr(G);
        for(int i=0;i<m;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            G[x][y]=G[y][x]=1;
        }
        if(n==1){
            printf("%d\n",c);
            continue;
        }
        V.clear();
        for(int i=1;i<=n;i++){
            if(!(p[i]==center)){
                p[i].ang=atan2(p[i].y-center.y,p[i].x-center.x);
                V.pb(i);
            }else{
                link[i]=i;
            }
        }
        sort(V.begin(),V.end(),cmp);
        sz=V.size();
        ll ans=0;
        int cnt=0;
        for(int i=0;i<sz;i++){
            for(int j=0;j<sz;j++){
                link[V[j]]=V[(i+j)%sz];
            }
            if(!check()) continue;
            cnt++;
            int cc=cal();
            ans=(ans+qpow(c,cc))%mod;
        }
        ans=(ans*inv(cnt))%mod;
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

 

posted @ 2015-11-03 00:34  PlusSeven  阅读(705)  评论(0编辑  收藏  举报