hdu 6960 / 2021“MINIEYE杯”中国大学生算法设计超级联赛(1)1011 Necklace of Beads(Burnside引理+数论)

https://acm.hdu.edu.cn/showproblem.php?pid=6960

题意:
用3种颜色(红绿蓝)的珠子构成项链,旋转相同看作相同,绿色珠子使用不超过k个。用n颗珠子能组成多少种颜色的项链。

根据Burnside引理,旋转相同即有n种置换,设\(H(i)\)表示旋转i个珠子的不动点个数,则 $$ans=\frac{1}{n}\sum_{i=0}^{n-1}H(i)$$
称旋转i个珠子的置换方式为置换 \(i\)
\(G(n,m)\)表示不考虑旋转相同时,在n颗珠子的圆环上有m颗绿色珠子的方案数,置换 \(i\) 会构成\(gcd(i,n)\)个长为\(\frac{n}{gcd(i,n)}\)的循环,这里证明可以看 https://www.cnblogs.com/TheRoadToTheGold/p/15080694.html
每个循环颜色都是一样的,即相当于求在\(gcd(i,n)\)颗珠子的圆环上,放\(\lfloor{\frac{k*gcd(i,n)}{n}}\rfloor\)颗绿色珠子,即$$H(i)=\sum_{j=0}^{\lfloor{\frac{k*gcd(i,n)}{n}}\rfloor}G(gcd(i,n),j)$$
那么

\[\begin{aligned} ans &=\frac{1}{n}\sum_{i=0}^{n-1} \sum_{j=0}^{\lfloor\frac{k * gcd(i,n)}{n}\rfloor}G(gcd(i,n),j) \\ &=\frac{1}{n}\sum_{d|n}\sum_{i=0}^{n-1}[gcd(i,n)=d]\sum_{j=0}^{\lfloor\frac{k * d}{n}\rfloor}G(d,j) \\ &=\frac{1}{n}\sum_{d|n}\sum_{i=1}^{n}[gcd(i,n)=d]\sum_{j=0}^{\lfloor\frac{k * d}{n}\rfloor}G(d,j) \\ &=\frac{1}{n}\sum_{d|n}\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}[gcd(\lfloor\frac{i}{d}\rfloor,n)=1]\sum_{j=0}^{\lfloor\frac{k * d}{n}\rfloor}G(d,j) \\ &=\frac{1}{n}\sum_{d|n}\Phi(\lfloor\frac{n}{d}\rfloor)\sum_{j=0}^{\lfloor\frac{k * d}{n}\rfloor}G(d,j) \\ \end{aligned} \\ \]

令$$F(d)=\sum_{j=0}^{\lfloor\frac{k * d}{n}\rfloor}G(d,j)$$
则$$ans=\frac{1}{n}\sum_{d|n}\Phi(\lfloor\frac{n}{d}\rfloor)F(d)$$
如何计算\(G(n,m)\)?
\(G(n,m)\)是不考虑旋转相同时,在n颗珠子的圆环上有m颗绿色珠子的方案数。每两颗绿色珠子之间要么是红蓝红蓝排列,要么是蓝红蓝红排列。即若m颗绿色珠子形成了m段间隙,假设绿色珠子的排列方式有\(D(m)\)种,则$$G(n,m)=D(m)*2^m$$
\(D(m)\)即在n个珠子构成的圆环上选m个不相邻的珠子的方案数
根据组合数学插空法可以以计算。
我们先计算n个珠子排成一排,选m个不相邻的珠子的方案数,即有n-m个珠子,用m个珠子插空,方案数为\(C(n-m+1,m)\)
现在是n个珠子构成一个圆环,如果定住第一颗珠子不选,方案数为\(C(n-m,m)\)。如果定住第一颗珠子选,方案数为\(C(n-m-1,m-1)\),这个可以先把n-m个珠子构成一个环,把第一颗珠子插进去,这样就只剩下n-m-1个空隙,还要插m-1个珠子
所以$$D(m)=C(n-m,m)+C(n-m-1,m-1)$$

\[G(n,m)=(C(n-m,m)+C(n-m-1,m-1))*2^m \]

#include<bits/stdc++.h>

using namespace std;

#define N 1000001

const int mod=998244353;

int p[N],tot;
int phi[N];
bool vis[N];

int n,k;

int pw[N],fac[N],invf[N],inv[N];

void pre()
{
	phi[1]=1;
	for(int i=2;i<N;++i)
	{
		if(!vis[i])
		{
			p[++tot]=i;
			phi[i]=i-1;
		}
		for(int j=1;j<=tot && i*p[j]<N;++j)
		{
			vis[i*p[j]]=true;
			if(!(i%p[j]))
			{
				phi[i*p[j]]=phi[i]*p[j];
				break;
			}
			else phi[i*p[j]]=phi[i]*(p[j]-1);
		}
	}
	inv[1]=1;
	for(int i=2;i<N;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	pw[0]=1;
	fac[0]=1;
	invf[0]=1;
	for(int i=1;i<N;++i) 
	{
		pw[i]=1ll*pw[i-1]*2%mod;
		fac[i]=1ll*fac[i-1]*i%mod;
		invf[i]=1ll*invf[i-1]*inv[i]%mod;
	}
	
}

int getC(int d,int m)
{	
	if(d<m) return 0;
	return 1ll*fac[d]*invf[m]%mod*invf[d-m]%mod;
}

int getG(int d,int m)
{
	if(!m)
	{
		if(d&1) return 0;
		return 2;
	}
	return 1ll*pw[m]*((getC(d-m,m)+getC(d-m-1,m-1))%mod)%mod;
}

int getF(int d)
{
	int m=1ll*k*d/n,f=0;
	for(int j=0;j<=m;++j) 
		f=(f+getG(d,j))%mod;
	return f;  
}

int main()
{
	pre();
	int T,ans;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&k);
		ans=0;
		for(int i=1;i*i<=n;++i)
		{
			if(n%i) continue;
			ans=(ans+1ll*phi[n/i]*getF(i)%mod)%mod;
			if(n/i!=i) ans=(ans+1ll*phi[i]*getF(n/i)%mod)%mod;
		}
		ans=1ll*ans*inv[n]%mod;
		printf("%d\n",ans);
	}
}
posted @ 2021-07-31 10:17  TRTTG  阅读(85)  评论(0编辑  收藏  举报