1. 程式人生 > >POJ 2409 ploya定理

POJ 2409 ploya定理

//題意:給定顏色種數和環上的珠子總數,問有多少種染色方案(通過旋轉和翻轉相同的算同一種)。
//
//分析:polya定理。在這裡只談一下polya定理是如何應用的。對於排成一排的帶編號的小球,按照某一種方案改變其中一些球的放置順
//序,可以稱之為置換。每一種置換方法可以用兩排數字來表示,第一排數字和第二排數字一一對應,第一排數字表示小球的原來位置(1~n),
//第二排數字表示小球交換後的位置。現在我們有n個小球,m種顏色。有k種置換方法,我們認為能通過置換方法交換位置後變成同一種染色情況
//(顏色的排列狀況相同,忽略小球編號),則我們認為這些互相通過置換能達到的狀態為同一種染色方法。我們現在要求總共有多少種染色方法。
//要計算方法數,我們先要計算k種置換方法中每種置換方法中含有的環數,即建立一個圖,有n個點,把每個置換方法兩排數字中的上下一一對應的數字
//對看成邊的起點和終點,計算這個圖中有幾個環。我們設環數分別為c1~ck。那麼染色方法數為(m^c1+m^c2+...+m^ck)/k。以上就是polya定理,這裡要
//注意的是置換方法集合必須是群,需要滿足封閉性,即如果把通過該集合中的若干個方法連續進行置換壓縮成一個置換方法(用兩排數子表示),那麼
//這種新的置換方法也必須屬於該集合。
//
//這道題可以這樣以來就是一道赤裸裸的polya定理題了。
//
//旋轉:
//n種旋轉方法每種旋轉i個格(1<=i<=n)迴圈結有gcd(i,n)個
//翻轉:
//
//(1)這種是經過某個頂點i與中心的連線為軸的翻轉,由於n為偶數,有對稱性,所以此種共n/2種翻轉:
//
//(2)這種是以頂點i和i+1的連線的中點與中心的連線為軸的翻轉,同樣,根據對稱性,也有n/2種翻轉:
//
//所以給定長度n,共有2n種置換。
//
//題意:用k種顏色對n個珠子構成的環上色,旋轉翻轉後相同的只算一種,求不等價的著色方案數。
//
//Burnside定理的應用:
//
//當n為奇數時,有n種翻轉,每種翻轉都是以一個頂點和該頂點對邊的中點對稱。有k^(n/2+1)*n種。
//
//當n為偶數時,有n種翻轉,其中一半是以兩個對應頂點,另一半是以兩條對邊對稱。有k^(n/2+1)*n/2+k^(n/2)*n/2種。
//
//考慮旋轉:列舉旋轉角度360/n*i,(0<i<=n),也就是一個置換。經過該置換,顏色仍保持不變的著色方案有k^GCD(n,i)種。
//
//
//
//一個長度為n的環,每i個上同一種顏色,可以上多少種顏色。
//
//假設起點在x,則x,x+i,x+2*i,……,x+k*i,……
//
//假設在第t次,第一次回到起點,則x=(x+t*i)%n => t*i%n=0 => t=LCM(i,n)/i=n*i/GCD(n,i)/i=n/GCD(n,i)。
//
//那麼可以上n/t種顏色,即n/(n/GCD(n,i))種,所以旋轉的著色方案有k^GCD(n,i)種
//
//
//大概學學

//(LL) m
//(ll)n;
//
//k為顏色數
//不同的方法數=幾個迴圈群數*pow(k,迴圈節數)之和  然後再除以所有的置換數之和
//  ans +=      n pow(k,(n+1)/2)                                      n+n  奇數
//                                                                    n+n/2+n/2 偶數
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<string>
#define ll long long
using namespace std;

ll gcd(ll a,ll b)
{
    if(b==0 ) return a;
    return gcd(b,a%b);
}

ll pow(int a,int b)
{
    return b==0?1:pow(a,b-1)*a;
}

int main()
{
    int n,k;
    while(scanf("%d%d",&k,&n)!=EOF)
    {
        if(n==0 && k==0)
            break;
        ll ans=0;
        if(n%2)
            ans+=n*pow(k,((n/2)+1));
        else
            ans+=n/2*pow(k,((n/2)+1))+n/2*pow(k,(n/2));


        for(int i=0;i<n;i++)
            ans+=pow(k,gcd(i,n));
        ans=ans/2/n;
    cout<<ans<<endl;
    }
}