POJ2888 - Magic Bracelet

Portal

Description

\(m(m\leq10)\)种颜色给一个\(n(n\leq10^9)\)个点的环染色,其中有\(k\)对颜色不能相邻。求在旋转同构下本质不同的方案数,对\(9973\)取模。

Solution

\(f(x)\)表示用\(m\)种颜色给一个\(x\)个点的环染色的在满足约束下的方案数。那么
\[\begin{align*} n\cdot ans&=\sum_{i=1}^n f(gcd(i,n)) \\ &= \sum_{d|n} f(d) \sum_{d|i}^n [gcd(i,n)=d] \\ &= \sum_{d|n} f(d) \sum_{i=1}^{\frac{n}{d}} [gcd(i,\frac{n}{d})=1] \\ &= \sum_{d|n} f(d) \varphi(\frac{n}{d}) \end{align*}\] \(\varphi(x)\)可以在对\(n\)分解质因数后简单求解,那么接下来只需要考虑如何求\(f(x)\)
\(dp[i][j]\)表示一个长度为\(i\)的序列以颜色\(j\)结尾时的合法方案数。构造转移矩阵\(M\),满足颜色\(i,j\)不能相邻时\(M_{i,j}=0\),其余的\(M_{i,j}=1\)。那么有\(dp[i]\times M=dp[i+1]\)\(dp[x]=dp[1]\times M^{x-1}\)
\(f(x)\)等于初始颜色为\(c\in[1,m]\),即\(dp[1][c]=1\)\(dp[x+1][c]\)的和。而\(dp[x+1][c]=\sum_{i=1}^m dp[1][i]\times M^x[i][c]=M^x[c][c]\),所以\(f(x)=\sum_{c=1}^m M^x[c][c]\)

时间复杂度\(O(\sigma_0(n)m^3logn)\)

Code

//Magic Bracelet
#include <cstdio>
#include <cstring>
inline char gc()
{
    static char now[1<<16],*s,*t;
    if(s==t) {t=(s=now)+fread(now,1,1<<16,stdin); if(s==t) return EOF;}
    return *s++;
}
inline int read()
{
    int x=0; char ch=gc();
    while(ch<'0'||'9'<ch) ch=gc();
    while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x;
}
const int N=2e6+10;
int prCnt,pr[N]; bool prNot[N];
void init(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(!prNot[i]) pr[++prCnt]=i;
        for(int j=1;j<=prCnt;j++)
        {
            if(i*pr[j]>n) break;
            prNot[i*pr[j]]=true;
            if(i%pr[j]==0) break;
        }
    }
}
const int P=9973;
int n,m,k;
int dCnt,dCnt1,d[2000],d1[50];
struct mtx
{
    int c,r,x[20][20];
    mtx(int _r,int _c) {r=_r,c=_c,memset(x,0,sizeof x);}
};
mtx operator *(mtx A,mtx B)
{
    mtx C=mtx(A.r,B.c);
    for(int i=1;i<=A.r;i++)
        for(int k=1;k<=A.c;k++)
            for(int j=1;j<=B.c;j++)
                C.x[i][j]=(C.x[i][j]+A.x[i][k]*B.x[k][j])%P;
    return C;
}
mtx I(int n)
{
    mtx A=mtx(n,n);
    for(int i=1;i<=n;i++) A.x[i][i]=1;
    return A;
}
mtx pow(mtx A,int y)
{
    mtx r=I(A.r),t=A;
    for(int i=y;i;i>>=1,t=t*t) if(i&1) r=r*t;
    return r;
}
int pow(int x,int y)
{
    int r=1,t=x%P;
    for(int i=y;i;i>>=1,t=t*t%P) if(i&1) r=r*t%P;
    return r;
}
int phi(int x)
{
    int r=x;
    for(int i=1;i<=dCnt1;i++)
        if(x%d1[i]==0) r=r/d1[i]*(d1[i]-1);
    return r%P;
}
int main()
{
    int task=read(); init(2e6);
    while(task--)
    {

    n=read(),m=read(),k=read();
    dCnt=0;
    for(int i=1;i*i<=n;i++) if(n%i==0) d[++dCnt]=i;
    int isSqr=(d[dCnt]*d[dCnt]==n);
    for(int i=1;i<=dCnt;i++) d[dCnt*2+(isSqr^1)-i]=n/d[i];
    dCnt+=dCnt-isSqr;
    dCnt1=0;
    for(int i=1,t=n;t>1;i++)
    {
        if(pr[i]*pr[i]>t) {d1[++dCnt1]=t; break;}
        if(t%pr[i]==0) d1[++dCnt1]=pr[i];
        while(t%pr[i]==0) t/=pr[i];
    }
    mtx M=mtx(m,m),Mk=mtx(m,m);
    for(int i=1;i<=m;i++)
        for(int j=1;j<=m;j++) M.x[i][j]=1;
    for(int i=1;i<=k;i++)
    {
        int c1=read(),c2=read();
        M.x[c1][c2]=M.x[c2][c1]=0;
    }
    int ans=0;
    for(int i=1;i<=dCnt;i++)
    {
        int f=0; Mk=pow(M,d[i]);
        for(int j=1;j<=m;j++) f=(f+Mk.x[j][j])%P;
        ans+=f*phi(n/d[i])%P; while(ans>=P) ans-=P;
    }
    printf("%d\n",ans*pow(n,P-2)%P);

    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/VisJiao/p/POJ2888.html
今日推荐