第一场1011(Polya)
2021杭电多校第一场1011(Polya) Problem - 6960 (hdu.edu.cn)
题意:
有红绿蓝三种颜色的珠子,现在给你n个珠子,绿色珠子不超过k种,问你可以组成不同的项链的的个数。其中相邻位置珠子的颜色不能相同,两种方案相同当且仅当其能通过二维平面上的旋转后相互重合。数据范围le6。
思路:
设\(f_{n,m}\)为绿色个数为m时,对大小为n的环染色的不考虑本质不同的方案数
由Pólya定理,旋转i次会将置换划分为\(\frac{n}{gcd(i,n)}\)段,每段的长为gcd(i,n)
\[ans=\frac{1}{n}\sum_{i=1}^n\sum_{j=0}^{\lfloor\frac{k\cdot gcd(i,n)}{n}\rfloor}f_{gcd(i,n),j}\\
=\frac{1}{n}\sum_{d/n}\sum_{i=1}^{n}[gcd(i,n)==d]\sum_{j=0}^{\lfloor\frac{k\cdot d}{n}\rfloor}f_{d,j}\\
=\frac{1}{n}\sum_{d/n}\sum_{i=1}^{\lfloor \frac{n}{d}\rfloor}[gcd(i,n)==1]\sum_{j=0}^{\lfloor\frac{k\cdot d}{n}\rfloor}f_{d,j}\\
=\frac{1}{n}\sum_{d/n}\phi(\lfloor\frac{n}{d}\rfloor)\sum_{j=0}^{\lfloor\frac{k\cdot d}{n}\rfloor}f_{d,j}\\
\]
考虑怎么求\(f_{n,m}\)
对一个环,假设我们选了位置n,那么位置n-1和1不能再选,方案数为\(\left(\matrix{n-m-1\\m-1}\right)\)
假设我们没选位置n,方案数为\(\left(\matrix{n-m\\m}\right)\)
中间穿插其中两种颜色珠子的方案数为\(2^m\)
所以\(f_{n,m}=2^m(\left(\matrix{n-m-1\\m-1}\right)+\left(\matrix{n-m\\m}\right))\)
当n为奇数,m=0时,不存在合法的方案
当n为偶数,m=0时,存在两种合法的方案
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int inv[1000010],fac[1000010],n,phi[1000010],prime[1000010],cnt;
bool bj[maxn+5];
const int mod=998244353;
int ksm(int a,int b)
{
int res=1;
while(b)
{
if(b&1)res=1ll*res*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return res;
}
int C(int n,int m)
{
int res=fac[n];
res=1ll*res*inv[m]%mod;
res=1ll*res*inv[n-m]%mod;
return res;
}
void get_phi(int x)
{
phi[1]=1;
for(int i=2;i<=x;i++)
{
if(!bj[i])prime[++cnt]=i,phi[i]=i-1;
for(int j=1;j<=cnt&&i*prime[j]<=x;j++)
{
bj[i*prime[j]]=1;
if(i%prime[j])phi[i*prime[j]]=1ll*phi[i]*phi[prime[j]]%mod;
else
{
phi[i*prime[j]]=1ll*phi[i]*prime[j]%mod;
break;
}
}
}
}
int f(int n,int k)
{
int res;
if(n&1)res=0;
else res=2;
for(int m=1;m<=min(k,n/2);m++)
{
res=(1ll*res+1ll*ksm(2,m)*(C(n-m,m)+C(n-m-1,m-1))%mod)%mod;
}
res%=mod;
res+=mod;
res%=mod;
return res;
}
int main()
{
inv[1]=1;inv[0]=1;fac[0]=1;
for(int i=2;i<=maxn;i++)
{
inv[i]=(mod-1ll*(mod/i)*inv[mod%i]%mod)%mod;
}
for(int i=1;i<=maxn;i++)
{
inv[i]=1ll*inv[i-1]*inv[i]%mod;
fac[i]=1ll*fac[i-1]*i%mod;
}
get_phi(maxn);
int t;
scanf("%d",&t);
while(t--)
{
int n,k;
scanf("%d%d",&n,&k);
long long ans=0;
for(int i=1;i*i<=n;i++)
{
if(n%i==0)
{
ans=(ans+1ll*phi[n/i]*f(i,1ll*i*k/n)%mod)%mod;
if(n/i!=i)
{
ans=(ans+1ll*phi[n/(n/i)]*f(n/i,1ll*n/i*k/n)%mod)%mod;
}
}
}
ans=ans*ksm(n,mod-2)%mod;
printf("%lld\n",ans);
}
return 0;
}