2022 杭电多校 第六场 K - Equipment Upgrade

题意

给定 \(n\), \(m\), 对于一个 \(l\) 长度, 元素都小于 \(m\) 的数组, 可以进行以下两种操作以任意顺序任意次

  • 将数组中所有元素模 \(m\) 意义下 \(+1\)
  • 将数组循环右移一格

现枚举长度为 \(i\) 的情况下, (\(1 \leq i \leq n\)) 求本质不同数组数, 答案对 \(998244353\) 取模

思路

首先, 对于操作 \(1\) 我们考虑对数组进行差分, 得到一个长度为 \(n\) 且模 \(m\) 意义下和为 \(0\) 的数组 ( 和为 \(km\) ), 即 \(b_i = a_i - a_{i-1}, (i \geq 1)\), \(b_0 = a_0 - a_{n-1}\), 且 \(\sum b_i = 0\), 那么很显然, 我们只需要任取 \(n-1\) 个元素, 最后一个元素补上即可

再考虑操作 \(2\)

由 burnside 引理, 有

\[|X / G| = \frac{1}{|G|}\sum_{i=0}^{n-1} f(n, \gcd(n, i)) \]

其中, \(f(n,i)\) 表示对于 \(n\) 元环, 旋转 \(i\) 次同构的种类数

含义是, 对于一个 \(n\) 圆环, 要计算其旋转 \(i\) 同构的种类数, 则有对于每个 \(j = j + i = j + 2i = \cdots\) 转化为, 每 \(i\) 个元素连一条边, 连边的元素均相等, 最后就会留下 \(\gcd(n,i)\) 个环, 对公式进行化简

\[\begin{align} |X / G|\cdot |G| &= \sum_{i=0}^{n-1} \sum_g [\gcd(n,i) = g] f(n, g) \\ &= \sum_g \sum_{i=0}^{\lfloor \frac{n-1}{g} \rfloor} [gcd(n,ig)=g] f(n,g) \\ &= \sum_g f(n,g) \sum_{i=0}^{\lfloor \frac{n-1}{g} \rfloor} [gcd(\frac{n}{g}, i) = 1] \end{align} \]

观察到, \(g\) 只需要枚举 \(n\) 的因子即可, 而第二个求和公式即是欧拉函数的定义: 小于等于 \(\frac{n}{g}\) 且与其互质的元素个数

\[\begin{align} |X / G|\cdot |G| &= \sum_{g|n} f(n,g) \cdot \phi(\frac{n}{g}) \\ |X / G| &= \frac{1}{|G|} \sum_{d|n} f(n,d) \cdot \phi(\frac{n}{d}) \end{align} \]

现考虑 \(f(n,d)\), 即, \(n\) 元环旋转 \(d\) 次同构, 且有 \(d\) 个环, 每个环大小为 \(\frac{n}{d}\), 求取方案数, 有

\[\begin{align} (a_1 + a_2 + \cdots + a_d) \cdot \frac{n}{d} &= k \cdot m \end{align} \]

注意到, \(\frac{n}{d}\) 可能与 \(m\) 有公因子, 而非公因子部分 \(\frac{n}{gd}\) 必然被 \(k\) 吸收, 记 \(g = \gcd(\frac{n}{d},m)\), 有

\[\begin{align} (a_1 + a_2 + \cdots + a_d) \cdot \frac{n}{gd} &= k \cdot \frac{m}{g} \\ a_1 + a_2 + \cdots + a_d &= k \cdot \frac{m}{d} \end{align} \]

那么有前 \(d - 1\) 个元素随便选, 最后一个元素补上, 由于可以选择 \([0, m-1]\) 的整数, 有 \(\frac{m}{m/g} = g\) 个选择, 则有

\[\begin{align} f(n,d) &= m^{d-1} \cdot \gcd(\frac{n}{d},m) \\ |X / G| &= \frac{1}{|G|} \sum_{d|n} m^{d-1} \cdot \gcd(\frac{n}{d},m) \cdot \phi(\frac{n}{d}) \end{align} \]

代码

只展示主要代码, 里面更换了枚举顺序, 先枚举 \(d\), 再枚举 \(n\), 先枚举 \(d\) 的倍数, 等效于枚举 \(n\) 的因子

#include <bits/stdc++.h>
#define de(x) std::cout << #x << " = " << (x) << std::endl
using i64 = long long;
constexpr int P = 998244353;
namespace MF { // math function

constexpr int N = 1 << 17;
int phi[N], phi_pre[N];
int n, prm[N];
bool notP[N] = {false};

void get_phi() {
    // get euler function
}

}

int mul(int x,int y) {
    return (i64) x * y % P;
}
int add(int x,int y) {
    x += y;
    if(x >= P) x -= P;
    if(x < 0) x += P;
    return x;
}
int qpow(int b,int p) {
    int res = 1;
    for(;p;p>>=1,b=(i64)b*b%P) if(p&1) res = (i64)res * b % P;
    return res;
}

void sol() {
    int N,m;
    std::cin >> N >> m;
    std::vector<int> res(N + 1);
    for(int d = 1; d <= N; ++d) {
        for(int n = d; n <= N; n += d) {
            res[n] = add(res[n], mul(qpow(m, d - 1), mul(std::__gcd(m, n / d), MF::phi[n / d])));
        }
    }
    for(int n = 1; n <= N; ++n) {
        std::cout << mul(res[n], qpow(n, P - 2)) << " \n"[n == N];
    }
}
posted @ 2022-08-10 10:09  LacLic  阅读(48)  评论(0编辑  收藏  举报