题目链接

https://lydsy.com/JudgeOnline/problem.php?id=1488

https://lydsy.com/JudgeOnline/problem.php?id=1815

题解

考虑polya,对于一个点的置换A1, ,AnA_1,\cdots ,A_n,假设循环节的长度分别为L1, ,LmL_1,\cdots ,L_m,那么边置换的循环节个数为
k=i=1mLi2+i=1mj=i+1mgcd(Li,Lj) k=\sum_{i=1}^m \lfloor \frac{L_i}{2}\rfloor+\sum_{i=1}^m \sum_{j=i+1}^m \gcd(L_i,L_j)
解释:考虑点置换的循环节内的边,显然跨度为xx的都只能是一种颜色,又由于xxLxL-x本质相同,所以这些边的循环节个数为L2\lfloor \frac{L}{2}\rfloor个;考虑点的循环节之间的边,对于两个长度分别为Li,LjL_i,L_j的循环节,边的循环节个数容(zhao)易(chu)证(gui)明(lv)是gcd(Li,Lj)\gcd(L_i,L_j)种。


假设现在找到了L1, ,LmL_1,\cdots,L_m,其中L1L2LmL_1\leq L_2\leq \cdots\leq L_m,满足循环节长度为ii的个数为SiS_i种,那么满足上述条件的点置换个数为
t=n!LiSi! t=\frac{n!}{\prod L_i\prod S_i!}
解释:考虑将每个置换看作一个圆排列,将nn个点放入大小分别为LiL_i的集合方案数为
(nL1)(nL1L2)(Lm1+LmLm)Si!=n!Li!Si! \frac{\binom{n}{L_1}\binom{n-L_1}{L_2}\cdots\binom{L_{m-1}+L_m}{L_m}}{\prod{S_i!}}=\frac{n!}{\prod L_i!\prod S_i!}
每个圆排列的方案数为
(Li1)! \prod (L_i-1)!


因此答案就是
1n!tCk \frac{1}{n!}\sum tC^k

代码

BZOJ 1488

#include <cstdio>

int read()
{
  int x=0,f=1;
  char ch=getchar();
  while((ch<'0')||(ch>'9'))
    {
      if(ch=='-')
        {
          f=-f;
        }
      ch=getchar();
    }
  while((ch>='0')&&(ch<='9'))
    {
      x=x*10+ch-'0';
      ch=getchar();
    }
  return x*f;
}

const int maxn=60;
const int mod=997;

int quickpow(int a,int b)
{
  int res=1;
  while(b)
    {
      if(b&1)
        {
          res=res*a%mod;
        }
      a=a*a%mod;
      b>>=1;
    }
  return res;
}

int n,l[maxn+2],tot,s[maxn+2],ans,inv[maxn+2],fac[maxn+2],ifac[maxn+2],g[maxn+2][maxn+2],power[maxn*maxn+2];

int getans()
{
  int cnt=fac[n],totc=0;
  for(int i=1; i<=tot; ++i)
    {
      cnt=cnt*inv[l[i]]%mod;
    }
  for(int i=1; i<=n; ++i)
    {
      cnt=cnt*ifac[s[i]]%mod;
    }
  for(int i=1; i<=tot; ++i)
    {
      totc+=l[i]/2;
    }
  for(int i=1; i<tot; ++i)
    {
      for(int j=i+1; j<=tot; ++j)
        {
          totc+=g[l[i]][l[j]];
        }
    }
  ans=(ans+cnt*power[totc])%mod;
  return 0;
}

int search(int now,int sum)
{
  int last=tot;
  if(now==1)
    {
      for(int i=1; i+sum<=n; ++i)
        {
          l[++tot]=1;
        }
      s[1]=n-sum;
      getans();
      tot=last;
      return 0;
    }
  for(int i=0; sum<=n; ++i,++s[l[++tot]=now],sum+=now)
    {
      search(now-1,sum);
    }
  tot=last;
  s[now]=0;
  return 0;
}

int gcd(int x,int y)
{
  return y?gcd(y,x%y):x;
}

int main()
{
  n=read();
  if(n==0)
    {
      puts("1");
      return 0;
    }
  inv[0]=inv[1]=1;
  for(int i=2; i<=n; ++i)
    {
      inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    }
  fac[0]=1;
  for(int i=1; i<=n; ++i)
    {
      fac[i]=fac[i-1]*i%mod;
    }
  ifac[0]=1;
  for(int i=1; i<=n; ++i)
    {
      ifac[i]=ifac[i-1]*inv[i]%mod;
    }
  for(int i=1; i<=n; ++i)
    {
      for(int j=1; j<=n; ++j)
        {
          g[i][j]=gcd(i,j);
        }
    }
  power[0]=1;
  for(int i=1; i<=n*n; ++i)
    {
      power[i]=power[i-1]<<1;
      if(power[i]>=mod)
        {
          power[i]-=mod;
        }
    }
  search(n,0);
  printf("%d\n",ans*ifac[n]%mod);
  return 0;
}

BZOJ 1815

#include <cstdio>

int read()
{
  int x=0,f=1;
  char ch=getchar();
  while((ch<'0')||(ch>'9'))
    {
      if(ch=='-')
        {
          f=-f;
        }
      ch=getchar();
    }
  while((ch>='0')&&(ch<='9'))
    {
      x=x*10+ch-'0';
      ch=getchar();
    }
  return x*f;
}

const int maxn=53;

int mod;

int quickpow(int a,int b)
{
  int res=1;
  while(b)
    {
      if(b&1)
        {
          res=res*a%mod;
        }
      a=a*a%mod;
      b>>=1;
    }
  return res;
}

int n,col,l[maxn+2],tot,s[maxn+2],ans,inv[maxn+2],fac[maxn+2],ifac[maxn+2],g[maxn+2][maxn+2],power[maxn*maxn+2];

int getans()
{
  int cnt=fac[n],totc=0;
  for(int i=1; i<=tot; ++i)
    {
      cnt=1ll*cnt*inv[l[i]]%mod;
    }
  for(int i=1; i<=n; ++i)
    {
      cnt=1ll*cnt*ifac[s[i]]%mod;
    }
  for(int i=1; i<=tot; ++i)
    {
      totc+=l[i]/2;
    }
  for(int i=1; i<tot; ++i)
    {
      for(int j=i+1; j<=tot; ++j)
        {
          totc+=g[l[i]][l[j]];
        }
    }
  ans=(ans+1ll*cnt*power[totc])%mod;
  return 0;
}

int search(int now,int sum)
{
  int last=tot;
  if(now==1)
    {
      for(int i=1; i+sum<=n; ++i)
        {
          l[++tot]=1;
        }
      s[1]=n-sum;
      getans();
      tot=last;
      return 0;
    }
  for(int i=0; sum<=n; ++i,++s[l[++tot]=now],sum+=now)
    {
      search(now-1,sum);
    }
  tot=last;
  s[now]=0;
  return 0;
}

int gcd(int x,int y)
{
  return y?gcd(y,x%y):x;
}

int main()
{
  n=read();
  col=read();
  mod=read();
  if(n==0)
    {
      puts("1");
      return 0;
    }
  inv[0]=inv[1]=1;
  for(int i=2; i<=n; ++i)
    {
      inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    }
  fac[0]=1;
  for(int i=1; i<=n; ++i)
    {
      fac[i]=1ll*fac[i-1]*i%mod;
    }
  ifac[0]=1;
  for(int i=1; i<=n; ++i)
    {
      ifac[i]=1ll*ifac[i-1]*inv[i]%mod;
    }
  for(int i=1; i<=n; ++i)
    {
      for(int j=1; j<=n; ++j)
        {
          g[i][j]=gcd(i,j);
        }
    }
  power[0]=1;
  for(int i=1; i<=n*n; ++i)
    {
      power[i]=1ll*power[i-1]*col%mod;
      if(power[i]>=mod)
        {
          power[i]-=mod;
        }
    }
  search(n,0);
  printf("%lld\n",1ll*ans*ifac[n]%mod);
  return 0;
}