[HDU6960]Necklace of Beads

题意

用红黄绿 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 3n106, 0k106


题解

由于要考虑旋转重复的情况,所以考虑使用 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=0n1j=0nkgcd(i,n)fgcd(i,n),j=n1dni=1n[gcd(i,n)=d]j=0nkdfd,j=n1dni=1dn[gcd(i,dn)=1]j=0nkdfd,j=n1dnφ(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 Cnmm ;若绿色填了第 n n n 位,则第 1 位和第 n − 1 n-1 n1 位均不能填,方案数为 C n − m − 1 m − 1 C_{n-m-1}^{m-1} Cnm1m1

综上可得 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(Cnm1m1+Cnmm) ,特别的 f n , 0 = 2 ⋅ [ 2 ∣ n ] f_{n,0}=2\cdot[2\mid n] fn,0=2[2n]

所以直接枚举所有约数暴力计算即可。 时间复杂度 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值