【LuoguP4916】魔力环

题目链接

题意

求出 n n n 个珠子的在旋转同构意义下的手 个数,满足以下条件:
恰好有 m m m 个黑色珠子,其余为白色。
黑色珠子形成的最长连续段不能超过 k k k 个。

Sol

考虑 B u r n s i d e Burnside Burnside 引理 / P o l y a /Polya /Polya 定理 , 那么答案就是:
∑ i = 1 n f ( i ) n \frac{\sum_{i=1}^n f(i)}{n} ni=1nf(i)

f ( i ) f(i) f(i) 表示在转 i i i 次的置换下所有合法染色方案中能够产生不动点的个数。这样我们就不用考虑旋转同构的问题了。

对于转 i i i 次的一个置换 , 显然它把这个长度为 n 的链分成了 g c d ( n , i ) gcd(n,i) gcd(n,i) 个环(也就是循环) , 那么一种方案要是不动点的话就必须满足这些环中的所有元素都是同色。
我们设 d = g c d ( n , i ) d=gcd(n,i) d=gcd(n,i) , 把式子再次变形:
a n s = 1 n ( ∑ d ∣ n n F ( d ) ∑ i = 1 n [ g c d ( i , n ) = d ] ) ans=\frac{1}{n} \bigg( \sum_{d|n}^n F(d) \sum_{i=1}^{n}\boldsymbol[gcd(i,n)=d \boldsymbol]\bigg) ans=n1(dnnF(d)i=1n[gcd(i,n)=d])
这是个常见的式子了, n n n 中与 i i i g c d gcd gcd d d d 的数有 φ ( n d ) \varphi(\frac{n}{d}) φ(dn) 个。

那么:

a n s = 1 n ( ∑ d ∣ n n F ( d ) φ ( n d ) ) ans=\frac{1}{n} \bigg( \sum_{d|n}^n F(d) \varphi(\frac{n}{d}) \bigg) ans=n1(dnnF(d)φ(dn))

F ( d ) F(d) F(d)表示现在有 n d \frac{n}{d} dn 个循环 , 模 d d d意义下在同一个剩余系中的珠子颜色要相同且黑色珠子不能有连续的超过 k k k 个 , 求合法的染色方案数。

因为当 d d d 确定下来的话 , 被分成的循环的情况也就确定了 , 模 d d d 相同的在一个环里。

既然这样 , 首先我们直接特判 n = m n=m n=m 的情况 , 这样由于染色的决策是一个循环的过程 , 每 d d d个珠子之间就可以看作是互不影响了 , 于是就相当于我们现在有 d d d 个珠子 , 要给其中 m n d = m d n \frac{m}{\frac{n}{d}}=\frac{md}{n} dnm=nmd 个染成黑色 , 黑色段最长不能超过 k k k 个的合法方案数了。

由于是一个环直接做不好考虑首尾 , 所以直接枚举首尾总共已经有 i i i 个球被染色 , 那么我们就希望求出 P a i n t ( n , m , k ) Paint(n,m,k) Paint(n,m,k) 表示有 n n n 个球排成一列 , 首尾已经都是白色的 , 现在要给中间的球染成黑色,满足黑色段最长不超过 k k k 的方案数。
由于球是等价的 , 这样我们可以把原问题变成 现在有 n − m n-m nm 个球 , 要在其中的 n − m − 1 n-m-1 nm1 个空隙里插入 m m m 个球 , 要求每个空隙里不能插入超过 k k k 个球 , 求方案数。

这是一个简单的容斥问题了 , 我们枚举至少有多少个空隙插入了多余 k k k 个球 , 剩下的就隔板法求一个方案数就行了。这里复杂度只有 O ( m k ) O(\frac{m}{k}) O(km)

然后我们由于要枚举环首尾有多少个黑珠子 , 所以单次计算一个 F F F 的复杂度是 O ( m ) O(m) O(m)

但是注意这里的 m m m 不是原来的 m m m 它是 m d n \frac{md}{n} nmd
如果我们用 d ′ d' d 来替换 n d \frac{n}{d} dn , 这样复杂度就是 O ( ∑ d ′ ∣ n m d ′ ) O(\sum_{d'|n}\frac{m}{d'}) O(dndm)
d ′ d' d 还必须是 m的约数 , 所以显然我们的复杂度是 g c d ( n , m ) gcd(n,m) gcd(n,m) 的约数和 ,可以轻松通过。

#include<bits/stdc++.h>
#define Set(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=1e6+10;
const int mod=998244353;
template <typename T> inline void init(T&x){
	x=0;char ch=getchar();bool t=0;
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
	if(t) x=-x;return;
}
typedef long long ll;
template <typename T>inline void Inc(T&x,int y){x+=y;if(x>=mod) x-=mod;return;}
template <typename T>inline void Dec(T&x,int y){x-=y;if(x <  0) x+=mod;return;}
template <typename T>inline int fpow(int x,T k){int ret=1;for(;k;k>>=1,x=(ll)x*x%mod) if(k&1) ret=(ll)ret*x%mod;return ret;}
int Sum(int x,int y){x+=y;if(x>=mod) return x-mod;return x;}
int Dif(int x,int y){x-=y;if(x < 0 ) return x+mod;return x;}
int n,m,k;
int gcd(int a,int b){return b? gcd(b,a%b):a;}
int pri[N],vis[N],cnt=0,phi[N],fac[N],finv[N];
inline int C(int n,int m){return n<m? 0:((ll)fac[n]*finv[m]%mod*finv[n-m]%mod);}
inline void Sieve(){
	phi[1]=1;vis[1]=1;fac[0]=finv[0]=fac[1]=finv[1]=1;
	int n=1e6;
	for(int i=2;i<=n;++i) {
		if(!vis[i]) pri[++cnt]=i,phi[i]=i-1;
		fac[i]=(ll)fac[i-1]*i%mod;
		for(int j=1;j<=cnt;++j){
			int nt=i*pri[j];if(nt>n) break;
			vis[nt]=1;
			if(i%pri[j]==0) {phi[nt]=phi[i]*pri[j];break;}
			phi[nt]=phi[i]*phi[pri[j]];
		}
	}
	finv[n]=fpow(fac[n],mod-2);
	for(int i=n-1;i;--i) finv[i]=(ll)finv[i+1]*(i+1)%mod;
	return;
}
inline int Solve(int n,int m,int k){
	if(!m) return 1;
	++k;int mx=m/k;int ans=0;
	for(int i=0;i<=mx;++i) {
		int ret=m-i*k;
		int res=(ll)C(n,i)*C(n+ret-1,n-1)%mod;
		if(i&1) Dec(ans,res);else Inc(ans,res);
	}
	return ans;
}
inline int Calc(int n,int m){
	int ret=0;int ed=min(m,k);
	for(int i=0;i<=ed;++i) Inc(ret,(ll)(i+1)*Solve(n-m-1,m-i,k)%mod);
	return ret;
}
int main()
{
	Sieve();init(n),init(m),init(k);
	if(n==m) {puts((k>=n)? "1":"0");return 0;}
	int ans=0;
	for(int i=1;i*i<=n;++i) {
		if(n%i==0) {
			if((m*i%n)==0) Inc(ans,(ll)Calc(i,m*i/n)*phi[n/i]%mod);
			if(n/i!=i&&m%i==0) Inc(ans,(ll)Calc(n/i,m/i)*phi[i]%mod);
		}
	}
	ans=(ll)ans*fpow(n,mod-2)%mod;
	printf("%d\n",ans);
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值