题意
用红黄绿 333 种颜色给一个有 nnn 个珠子的环形项链染色,要求相邻珠子颜色不同且绿色珠子个数小于等于 kkk ,求本质不同的染色方案。
3≤n≤106, 0≤k≤1063\le n\le 10^6,\ 0\le k\le 10^63≤n≤106, 0≤k≤106
题解
由于要考虑旋转重复的情况,所以考虑使用 Polya\rm PolyaPolya 定理,设 fn,mf_{n,m}fn,m 表示长度为 nnn 的环且绿色个数为 mmm 时且不考虑旋转的方案数。对于旋转 iii 位的置换,循环置换的数量为 gcd(i,n)\gcd(i,n)gcd(i,n) ,所以
ans=1n∑i=0n−1∑j=0⌊kgcd(i,n)n⌋fgcd(i,n),j=1n∑d∣n∑i=1n[gcd(i,n)=d]∑j=0⌊kdn⌋fd,j=1n∑d∣n∑i=1nd[gcd(i,nd)=1]∑j=0⌊kdn⌋fd,j=1n∑d∣nφ(nd)F(d,⌊kdn⌋)
\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⌋)
考虑如何求 fn,mf_{n,m}fn,m 。由于 mmm 个绿色珠子会把环分成 mmm 个连续段,每个段都只有 222 种方案(红黄红黄…\ldots…,黄红黄红…\ldots…),故方案数为 2m2^m2m 。考虑插空法,若绿色没有填在第 nnn 位,方案数为 Cn−mmC_{n-m}^mCn−mm ;若绿色填了第 nnn 位,则第 1 位和第 n−1n-1n−1 位均不能填,方案数为 Cn−m−1m−1C_{n-m-1}^{m-1}Cn−m−1m−1 。
综上可得 fn,m=2m(Cn−m−1m−1+Cn−mm)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) ,特别的 fn,0=2⋅[2∣n]f_{n,0}=2\cdot[2\mid n]fn,0=2⋅[2∣n] 。
所以直接枚举所有约数暴力计算即可。 时间复杂度 O(nloglogn)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;
}