POJ 2888 Magic Bracelet

題意:用m種不同顏色的珠子連成一條長爲n的項鍊,其中,有k對珠子不能相鄰,問總共有多少種(mod 9973)n<10^9,m<=10

題解:組合計數也就burning和polya了,這題用的是Burning Side。

考慮在一種置換f下的穩定核方法,由於只有旋轉對稱,如果是旋轉k個珠子,那麼穩定核的循環節也就是gcd(n,k)=r,枚舉k的話是不現實的,那麼只有枚舉r,即n的所有約數。gcd(n,k)=r,即gcd(n/r,k/r)=1,也就是與n/r互質的數的個數(歐拉函數)就是循環節爲r的置換個數。

對循環節爲r的情況,需要考慮循環節內部珠子的排列,使它們滿足題目要求,還要考慮第一個珠子與最後一個珠子是否滿足要求(最後一個珠子的下一個珠子也是下一輪的第一個珠子),由於珠子種類只有10,可以用鄰接矩陣map[i][j]表示i,j兩種珠子是否能相鄰,如果能,map[i][j]=1,反之,map[i][j]=0,這樣的話,離散數學老師應該說過,如果用0,1矩陣A來表示無向圖的連通情況的話,A^k代表的就是一個點經過k條路後能到達的地方的方法數。

因此,對於循環節爲r的情況,A^r就是任意點經過r條路能到達的地方,與之對應的map[i][i]就是一個珠子經過可行路徑轉了r條路徑又回到自己的種數,其實,就是前面說的滿足題意的排列數,矩陣乘法可以分治加個速,對於n的約數隨便求一下,這道題就出來了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int mr=100000;
const LL mod=9973;
bool notp[mr];
int pr[mr],fac[102],num[102];
int pn,top,n,m;
LL ans;
struct MAT
{
    LL bas[13][13];
    void init()
    {
        memset(bas,0,sizeof(bas));
    }
} mat[50];
MAT mul(MAT a,MAT b)
{
    MAT c;
    c.init();
    for(int i=1; i<=m; i++)
        for(int k=1; k<=m; k++)
        {
            if(a.bas[i][k])
            {
                for(int j=1; j<=m; j++)
                {
                    c.bas[i][j]+=a.bas[i][k]*b.bas[k][j];
                    if(c.bas[i][j]>=mod)
                        c.bas[i][j]%=mod;
                }
            }
        }
    return c;
}
void getpri()//篩素數
{
    pn=0;
    memset(notp,0,sizeof(notp));
    for(int i=2; i<mr; i++)
    {
        if(!notp[i])
        {
            pr[pn++]=i;
        }
        for(int j=0; j<pn && i*pr[j]<mr; j++)
        {
            int k=i*pr[j];
            notp[k]=1;
            if(i%pr[j]==0)break;
        }
    }
}
void divn()
{
    int nn=n;
    top=0;
    int lim=(int)sqrt((double(nn)))+1;
    for(int i=0; pr[i]<=lim; i++)
    {
        if(nn%pr[i]==0)
        {
            fac[top]=pr[i];
            num[top]=0;
            while(nn%pr[i]==0)
                num[top]++,nn/=pr[i];
            top++;
        }
    }
    if(nn>1)
        fac[top]=nn,num[top++]=1;
}
int phi(int x)
{
    int i, res=x;
    for (i=0;pr[i]<(int)sqrt((double)x)+1;i++)
    if(x%pr[i]==0)
    {
        res=res/pr[i]*(pr[i]-1);
        while(x%pr[i]==0)x/=pr[i];
    }
    if(x>1)res=res/x*(x-1);
    return res;
}
void solve(int r)
{
    int res=phi(n/r);
    MAT mt;
    mt.init();
    for(int i=1;i<=m;i++)
        mt.bas[i][i]=1;
    for(int i=1,tp=r;tp;i++,tp>>=1)
    if(tp&1)mt=mul(mt,mat[i]);
    for(int i=1;i<=m;i++)
    {
        ans+=mt.bas[i][i]*res;
        if(ans>=mod)ans%=mod;
    }
}
void dfs(int id,int sum)
{
    if(id==top)
    {
        solve(sum);
        return;
    }
    else
    {
        dfs(id+1,sum);
        for(int ct=0; ct<num[id]; ct++)
            dfs(id+1,sum=sum*fac[id]);
    }
}
void init()
{
    for(int i=2; i<50; i++)
        mat[i]=mul(mat[i-1],mat[i-1]);
}
int Egcd (int a,int b, int &x, int &y)
{
    if (b==0)
    {
        x=1,y=0;
        return a;
    }
    LL d, tp;
    d = Egcd (b, a%b, x, y);
    tp = x;
    x = y;
    y = tp - a/b*y;
    return d;
}
int getni()
{
    int x,y;
    Egcd(n,mod,x,y);
    return (x%mod+mod)%mod;
}
int main()
{
    getpri();
    int T;
    for(scanf("%d",&T); T; T--)
    {
        int k;
        scanf("%d%d%d",&n,&m,&k);
        ans=0;
        for(int i=1; i<=m; i++)
            for(int j=1; j<=m; j++)
                mat[1].bas[i][j]=1;
        for(int a,b,i=0; i<k; i++)
        {
            scanf("%d%d",&a,&b);
            mat[1].bas[a][b]=mat[1].bas[b][a]=0;
        }
        init();
        divn();
        dfs(0,1);
        printf("%d\n",ans*getni()%mod);
    }
    return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章