POJ 2888 Magic Bracelet

版权声明:写得不好,转载请通知一声,还请注明出处,感激不尽 https://blog.csdn.net/As_A_Kid/article/details/82217065

Problem

POJ

Solution

由于有染色限制,那我们不方便直接用polya计数。不妨先从最朴素的DP考虑起,设f[i][j]表示染了前i个珠子,且第i个珠子的颜色为j的方案数,由于是环形的,你需要dp到n+1。为了避免重复计算,我们套用一下Burnside引理。

不妨称旋转i个的置换称为置换i。考虑在置换i下,循环节数为 g c d ( n , i ) ,那么我们的dp就求得到 g c d ( n , i ) + 1 的位置即可。因为前1..gcd(n,i)个珠子显然两两不属于同一个循环,那么这样实质上是枚举所有循环节的颜色。然而gcd(n,i)最大还是能达到n,考虑到m比较小,我们用矩阵快速幂优化即可。我们也可以省去枚举第一个珠子颜色的复杂度,如果你模拟一遍,就会发现其实枚举第i种颜色,答案就是转移矩阵做完快速幂后的第(i,i)号元素,那么直接统计对角线上的值即可。

还有一个比较套路的优化,当循环节数相等时,答案是一样的。则枚举n的约数d,然后有 ϕ ( n d ) 个。

时间复杂度 O ( m 3 n log n )

Code

#include <cstring>
#include <cstdio>
#define rg register
using namespace std;
typedef long long ll;
const int maxn=50,mod=9973;
template <typename Tp> inline void getmin(Tp &x,Tp y){if(y<x) x=y;}
template <typename Tp> inline void getmax(Tp &x,Tp y){if(y>x) x=y;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
inline int pls(int x,int y){return x+y>=mod?x+y-mod:x+y;}
struct Matrix{
    int n,m,a[maxn][maxn];
    int* operator [] (int x){return a[x];}
    void clear(){n=m=0;memset(a,0,sizeof(a));}
    Matrix operator * (Matrix &b)const
    {
        Matrix c;c.clear();
        if(m!=b.n) return c;
        c.n=n;c.m=b.m;
        for(int i=1;i<=n;i++)
          for(rg int k=1;k<=b.m;k++)
            for(rg int j=1;j<=m;j++)
              c[i][k]=pls(c[i][k],a[i][j]*b[j][k]%mod);
        return c;
    }
}a,b;
int z,n,m,k,tot,ans,vis[100010],pri[100010];
int power(int x,int y)
{
    int res=1;
    for(;y;y>>=1,x=x*x%mod)
      if(y&1)
        res=res*x%mod;
    return res;
}
Matrix mat_pow(Matrix a,int y)
{
    Matrix res;res.clear();
    res.n=res.m=a.n;
    for(rg int i=1;i<=a.n;i++) res[i][i]=1;
    for(;y;y>>=1,a=a*a)
      if(y&1)
        res=res*a;
    return res;
}
void init()
{
    for(rg int i=2;i<=100000;i++)
    {
        if(!vis[i]) pri[++tot]=i;
        for(int j=1;j<=tot&&i*pri[j]<=100000;j++)
        {
            vis[i*pri[j]]=1;
            if(i%pri[j]==0) break;
        }
    }
}
void input()
{
    int x,y;
    read(n);read(m);read(k);
    a.n=a.m=m;ans=0;
    for(rg int i=1;i<=m;i++)
      for(rg int j=1;j<=m;j++)
        a[i][j]=1;
    while(k--)
    {
        read(x);read(y);
        a[x][y]=a[y][x]=0;
    }
}
int phi(int x)
{
    int res=x;
    for(rg int i=1;pri[i]*pri[i]<=x;i++)
      if(x%pri[i]==0)
      {
        res=res-res/pri[i];
        while(x%pri[i]==0) x/=pri[i];
      }
    if(x^1) res=res-res/x;
    return res%mod;
}
int calc(int x)
{
    int res=0;
    b=mat_pow(a,x);
    for(rg int i=1;i<=m;i++) res=pls(res,b[i][i]);
    res=res*phi(n/x)%mod;
    return res;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    init();
    read(z);
    while(z--)
    {
        input();
        for(rg int i=1;i*i<=n;i++)
          if(n%i==0)
          {
            ans=pls(ans,calc(i));
            if(i*i!=n) ans=pls(ans,calc(n/i));
          }
        printf("%d\n",ans*power(n%mod,mod-2)%mod);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/As_A_Kid/article/details/82217065