题解 P4916 【魔力环】
command_block · · 题解
题意:
- 有
m 个黑色珠子,n-m 个白色珠子。 - 不存在大于
k 个连续的黑色珠子。
两个手环旋转后相同,则本质相同。求本质不同的手环的种类数。
答案对
upd 2025.2.27:重新排版,修改了一处错误。
首先特判全黑的情况(
看到环与旋转,容易想到 Burnside 引理。
设
- 引理:长度为
n 的环旋转t 步后和自己相等。等价关系形成\gcd(t,n) 个环,这些环大小相等,均匀交错。
设
现在考虑如何求
等价类(形如交错的环)的个数为
如下
0123 | 0123 | 0123
那么每连续
- 进一步地,可证
\sigma(m)=O(m\log\log m) 。
代码如下:
#include <algorithm>
#include <cstdio>
const int MaxN = 100050, mod = 998244353;
int powM(int a, int t=mod-2) {
int ret = 1;
while(t) {
if (t&1)
ret = 1ll*ret*a %mod;
a = 1ll*a*a %mod;
t >>= 1;
}
return ret;
}
int fac[MaxN], ifac[MaxN];
int C(int n, int m) {
return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
void Init(int n) {
fac[0] = 1;
for (int i=1; i<=n; i++)
fac[i] = 1ll*fac[i-1]*i %mod;
ifac[n] = powM(fac[n]);
for (int i=n; i; i--)
ifac[i-1] = 1ll*ifac[i]*i %mod;
}
int T(int n,int m) {
return C(n+m-1, n-1);
}
int K;
int R(int n,int m) {
int ans=0;
for (int i=0; i<=std::min(m/(K+1),n); i++) {
if (i&1)
ans =(ans-1ll*C(n,i)*T(n,m-i*(K+1))) %mod;
else
ans =(ans+1ll*C(n,i)*T(n,m-i*(K+1))) %mod;
}
return (ans+mod)%mod;
}
int S(int n, int m) {
if (m<=K) return C(n,m);
int ans = 0;
for (int i=0; i<=std::min(K,m); i++)
ans = (ans+1ll*R(n-m-1,m-i)*(i+1)) %mod;
return ans;
}
int phi(int n) {
int ans = n;
for (int i=2; i*i<=n; i++)
if (n%i==0) {
ans=ans/i*(i-1);
while(n%i==0)n/=i;
}
if (n>1) ans = ans/n*(n-1);
return ans;
}
int gcd(int a, int b) {
return !b ? a : gcd(b, a%b);
}
int main() {
int n, m;
scanf("%d%d%d", &n, &m, &K);
if (n==m) {
puts(K>=m ? "1" : "0");
return 0;
}
Init(n);
int D = gcd(n,m), ans = 0;
auto calcFactor = [&ans,n,m](int d) -> void {
ans = (ans + 1ll*S(n/d,m/d)*phi(d)) %mod;
};
for (int i=1; i*i<=D; i++)
if (D%i==0) {
calcFactor(i);
if (i*i!=D)
calcFactor(D/i);
}
ans = 1ll*ans*powM(n)%mod;
printf("%d", ans);
return 0;
}