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;
}



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