【洛谷P4916】魔力环(Burnside)(组合数学)

传送门


题解:

看到旋转同构,考虑Burnside。

旋转同构中常见的phi的转化就不提了。

直接考虑循环个数为 d d d的时候的答案。

显然只有当 d ∣ n d\mid n dn d ∣ m d\mid m dm的时候答案非0。

特判掉 n = m n=m n=m m = 0 m=0 m=0的情况。

现在环上必然有黑色和白色。

N = n / d , M = m / d N=n/d,M=m/d N=n/d,M=m/d,接下来分类讨论。

如果 M ≤ K M\leq K MK,随便放就行了,反正有白色,黑色连不起来。

否则,我们断环为链,把所有白色拿出来,枚举断掉的地方首尾链接起来有多少黑色的,然后把剩下的黑色分成若干组,每组可以为空但是不允许超过 K K K。直接枚举有多少组超过 K K K暴力容斥即可。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int mod=998244353;
inline int add(int a,int b){a+=b-mod;return a+(a>>31&mod);}
inline int dec(int a,int b){a-=b;return a+(a>>31&mod);}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b,int res=1){
	for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
	return res;
}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}

int n,m,K;

cs int N=1e5+7;

int p[N],pc;
bool mark[N];
int phi[N];

inline void sieves(int n){
	phi[1]=1;
	for(int re i=2;i<=n;++i){
		if(!mark[i])p[++pc]=i,phi[i]=i-1;
		for(int re j=1;i*p[j]<=n;++j){
			mark[i*p[j]]=true;
			if(i%p[j]){phi[i*p[j]]=phi[i]*(p[j]-1);}
			else {
				phi[i*p[j]]=phi[i]*p[j];
				break;
			}
		}
	}
}

int fac[N<<1],ifac[N<<1];
inline void init_inv(int n){
	fac[0]=fac[1]=1;
	for(int re i=2;i<=n;++i)fac[i]=mul(fac[i-1],i);
	ifac[n]=power(fac[n],mod-2);
	for(int re i=n-1;~i;--i)ifac[i]=mul(ifac[i+1],i+1);
}

inline int C(int n,int m){return n>=0&&m>=0&&n>=m?mul(fac[n],mul(ifac[m],ifac[n-m])):0;}
inline int calc(int m,int n){return C(n+m-1,n-1);}

inline int Put(int m,int n,int k){
	if(m<0)return 0;
	int res=0;
	for(int re i=0;i<=n;++i)
	if(m>=i*(k+1)){
		int val=mul(calc(m-i*(k+1),n),C(n,i));
		(i&1)?Dec(res,val):Inc(res,val);
	}else break;
	return res;
}

inline int calc(int d){
	int n=::n/d,m=::m/d;
	if(m<=K)return C(n,m);
	int res=0;
	for(int re i=0;i<=K;++i)
	Inc(res,mul(i+1,Put(m-i,n-m-1,K)));
	return res;
}

signed main(){
#ifdef zxyoi
//	freopen("loop.in","r",stdin);
#endif
	scanf("%d%d%d",&n,&m,&K);
	if(n==m){
		cout<<(K==n)<<"\n";
		return 0;
	}
	if(!m)return cout<<"1\n",0;
	sieves(n);init_inv(n<<1);
	int g=std::__gcd(n,m),ans=0;
	for(int re i=1;i*i<=g;++i)if(g%i==0){
		Inc(ans,mul(phi[i],calc(i)));
		if(i*i!=g)Inc(ans,mul(calc(g/i),phi[g/i]));
	}
	cout<<power(n,mod-2,ans)<<"\n"; 
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值