题意
用红黄绿 3 3 3 种颜色给一个有 n n n 个珠子的环形项链染色,要求相邻珠子颜色不同且绿色珠子个数小于等于 k k k ,求本质不同的染色方案。
3 ≤ n ≤ 1 0 6 , 0 ≤ k ≤ 1 0 6 3\le n\le 10^6,\ 0\le k\le 10^6 3≤n≤106, 0≤k≤106
题解
由于要考虑旋转重复的情况,所以考虑使用
P
o
l
y
a
\rm Polya
Polya 定理,设
f
n
,
m
f_{n,m}
fn,m 表示长度为
n
n
n 的环且绿色个数为
m
m
m 时且不考虑旋转的方案数。对于旋转
i
i
i 位的置换,循环置换的数量为
gcd
(
i
,
n
)
\gcd(i,n)
gcd(i,n) ,所以
a
n
s
=
1
n
∑
i
=
0
n
−
1
∑
j
=
0
⌊
k
gcd
(
i
,
n
)
n
⌋
f
gcd
(
i
,
n
)
,
j
=
1
n
∑
d
∣
n
∑
i
=
1
n
[
gcd
(
i
,
n
)
=
d
]
∑
j
=
0
⌊
k
d
n
⌋
f
d
,
j
=
1
n
∑
d
∣
n
∑
i
=
1
n
d
[
gcd
(
i
,
n
d
)
=
1
]
∑
j
=
0
⌊
k
d
n
⌋
f
d
,
j
=
1
n
∑
d
∣
n
φ
(
n
d
)
F
(
d
,
⌊
k
d
n
⌋
)
\begin{aligned} ans&=\frac1n\sum_{i=0}^{n-1}\sum_{j=0}^{\lfloor\frac{k\gcd(i,n)}n\rfloor}f_{\gcd(i,n),j}\\ &=\frac1n\sum_{d\mid n}\sum_{i=1}^n[\gcd(i,n)=d]\sum_{j=0}^{\lfloor \frac{kd}n\rfloor}f_{d,j}\\ &=\frac1n\sum_{d\mid n}\sum_{i=1}^{\frac nd}[\gcd(i,\frac nd)=1]\sum_{j=0}^{\lfloor \frac{kd}n\rfloor}f_{d,j}\\ &=\frac1n\sum_{d\mid n}\varphi(\frac nd)F(d,\lfloor \frac{kd}n\rfloor) \end{aligned}
ans=n1i=0∑n−1j=0∑⌊nkgcd(i,n)⌋fgcd(i,n),j=n1d∣n∑i=1∑n[gcd(i,n)=d]j=0∑⌊nkd⌋fd,j=n1d∣n∑i=1∑dn[gcd(i,dn)=1]j=0∑⌊nkd⌋fd,j=n1d∣n∑φ(dn)F(d,⌊nkd⌋)
考虑如何求 f n , m f_{n,m} fn,m 。由于 m m m 个绿色珠子会把环分成 m m m 个连续段,每个段都只有 2 2 2 种方案(红黄红黄 … \ldots …,黄红黄红 … \ldots …),故方案数为 2 m 2^m 2m 。考虑插空法,若绿色没有填在第 n n n 位,方案数为 C n − m m C_{n-m}^m Cn−mm ;若绿色填了第 n n n 位,则第 1 位和第 n − 1 n-1 n−1 位均不能填,方案数为 C n − m − 1 m − 1 C_{n-m-1}^{m-1} Cn−m−1m−1 。
综上可得 f n , m = 2 m ( C n − m − 1 m − 1 + C n − m m ) f_{n,m}=2^m\big(C_{n-m-1}^{m-1}+C_{n-m}^{m}\big) fn,m=2m(Cn−m−1m−1+Cn−mm) ,特别的 f n , 0 = 2 ⋅ [ 2 ∣ n ] f_{n,0}=2\cdot[2\mid n] fn,0=2⋅[2∣n] 。
所以直接枚举所有约数暴力计算即可。 时间复杂度 O ( n log log n ) O(n\log\log n) O(nloglogn)
#include <bits/stdc++.h>
const int N = 1e6 + 5, P = 998244353;
using namespace std;
using arr = int[N];
using ll = long long;
arr fac, ifac, inv, pr, phi, pw;
inline void Init(int n) {
vector<char> vis(n + 1, 0);
fac[0] = fac[1] = ifac[0] = ifac[1] = inv[1] = phi[1] = pw[0] = 1,
pw[1] = 2;
for (int i = 2; i <= n; ++i) {
if (!vis[i])
pr[++pr[0]] = i, phi[i] = i - 1;
for (int j = 1, x; j <= pr[0] && (x = i * pr[j]) <= n; ++j) {
vis[x] = 1, phi[x] = phi[i];
if (i % pr[j])
phi[x] *= pr[j] - 1;
else {
phi[x] *= pr[j];
break;
}
}
pw[i] = (pw[i - 1] << 1ll) % P;
inv[i] = (ll)(P - P / i) * inv[P % i] % P;
fac[i] = (ll)fac[i - 1] * i % P;
ifac[i] = (ll)ifac[i - 1] * inv[i] % P;
}
}
inline ll C(int n, int m) {
return n >= m ? (ll)fac[n] * ifac[m] % P * ifac[n - m] % P : 0ll;
}
inline ll F(int n, int k) {
ll f = n & 1 ? 0 : 2;
for (int m = 1; m <= min(k, n / 2); ++m)
f += pw[m] * (C(n - m, m) + C(n - m - 1, m - 1)) % P;
return f % P;
}
int n, k;
inline void Solve() {
ll Ans = 0;
for (int d = 1; d <= n; ++d)
if (n % d == 0)
Ans += phi[n / d] * F(d, (ll)k * d / n) % P;
printf("%lld\n", Ans % P * inv[n] % P);
}
int main() {
Init(1e6);
scanf("%*d");
while (~scanf("%d%d", &n, &k))
Solve();
return 0;
}