第一场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;
}
posted @ 2021-08-20 16:55  1427314831a  阅读(39)  评论(0编辑  收藏  举报