zoukankan      html  css  js  c++  java
  • 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}} floor)颗绿色珠子,即$$H(i)=sum_{j=0}^{lfloor{frac{k*gcd(i,n)}{n}} floor}G(gcd(i,n),j)$$
    那么

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

    令$$F(d)=sum_{j=0}^{lfloorfrac{k * d}{n} floor}G(d,j)$$
    则$$ans=frac{1}{n}sum_{d|n}Phi(lfloorfrac{n}{d} floor)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
    ",ans);
    	}
    }
    
    作者:xxy
    本文版权归作者和博客园共有,转载请用链接,请勿原文转载,Thanks♪(・ω・)ノ。
  • 相关阅读:
    StringBuffer
    判断java标识符
    基本数据类型和String相互转换
    使用String
    Properties属性文件
    Map排序
    java集合之三映射:HashMap、Hashtable、LinkedHashMap、TreeMap
    列表、集合与数组之间相互转换
    自定义列表排序
    ML-支持向量:SVM、SVC、SVR、SMO原理推导及实现
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/15083475.html
Copyright © 2011-2022 走看看