「解题报告」ARC126F Affine Sort
目前为止在 ARC 做到过的最震撼的数学题。
我们先把 \(f(K)\) 改写一下,设 \(g(K)\) 表示当 \(c=K\) 时合法的 \((a, b)\) 二元组数,那么就有:
那么根据 O'Stolz 定理 我们要求的式子为:
O'Stolz 定理:
简单来说就是洛必达法则的离散版本,可以用于求数列极限。
假如有数列 \(\{a_n\}, \{b_n\}\) 满足:
- \(\{b_n\}\) 严格单调递增;
- \(\lim_{n \to \infty} b_n = +\infty\);
那么有:
\[\lim_{n\to \infty} \frac{a_n}{b_n} = \lim_{n \to \infty} \frac{a_{n + 1} - a_n}{b_{n + 1} - b_n} \]
那么接下来我们只需要求出 \(\lim_{K \to \infty}\frac{g(K)}{K^2}\) 即可。
我们不妨把问题转换一下:
而由于 \(K \to \infty\),那么我们可以理解为 \(\frac{a}{K}, \frac{b}{K}\) 取遍 \([0, 1]\) 内的所有实数。
那么假如设 \(\alpha = \frac{a}{K}, \beta = \frac{b}{K}\),并且将所有合法的 \((\alpha, \beta)\) 看做一个二维平面上的一个大小为 \(\frac{1}{K^2}\) 的区域,那么我们要求的 \(\frac{g(K)}{K^2}\) 就是求这个二维平面的面积。当 \(K \to \infty\) 时,我们就可以把每一个 \(\frac{1}{K^2}\) 的面积近似的看做一个点,那么现在我们相当于就是求这个二维平面上的积分了。实际上,根据测度论的相关知识,可以证明两者的极限是相等的。
那么我们现在就把 \(\alpha, \beta\) 看做是 \([0, 1]\) 上的实数来考虑这个问题。
我们设 \(\{x\}\) 为 \(x\) 的小数部分,即 \(x \bmod 1 = x - \lfloor x\rfloor\)。
对于\(\bmod 1\) 的问题,我们可以将其放到一个环上来考虑,然后将每个数看做环上的点。那么我们发现,\(\beta\) 的作用就是将整个环旋转了若干位置。假如此时 \(\{\alpha X_i\}\) 从某一位置开始是有序的,那么我们就可以通过一些旋转使得他们从 \(0\) 开始是有序的。这相当于将 \(0\) 的位置旋转到 \(\{\alpha X_1\}\) 与 \(\{\alpha X_n\}\) 之间,那么对于某一个固定的 \(\alpha\),合法的 \(\beta\) 的积分为 \(\{\alpha(X_1-X_n)\}\)。
那么我们如何确定对于哪些 \(\alpha\) 来说,\(\{\alpha X_i\}\) 从某一位置开始是有序的呢?我们可以使用距离之和来刻画这件事情。设 \(d_k(\alpha) = \{\alpha(X_{k \bmod n + 1} - X_k)\}\),那么有序当且仅当 \(D(\alpha)=\sum_{k=1}^n d_k(\alpha) = 1\)。
那么我们考虑 \(D(\alpha)\) 在哪里取到 \(1\),对这些 \(\alpha\) 计算上述的 \(\beta\) 的积分即可。
同时,我们注意到斜率 \(\sum_{i=1}^n (X_{k \bmod n + 1} - X_k) = 0\),也就是说 \(D(\alpha)\) 的图像为若干条横着的直线组成的,那么如果我们找出所有 \(y=1\) 的直线的区域,对这些区域进行积分,就能计算出答案了。
我们先考虑一个单独的函数 \(f(x) = \{kx\}\) 长什么样子。发现这就是一个一次函数,并在有限个位置进行了 \(\pm 1\) 的值的变化。
而我们发现,这样的变化的位置的数量是 \(O(\sum X_i)\) 的,那么我们就可以暴力维护出所有这样的点,这样就能维护出 \(D(\alpha)\) 的图像了。找到图像之后,对每一段 \(y=1\) 的位置求 \(\int_{l}^r \{\alpha(X_1 - X_n)\}\)。这个积分同样可以从间断点断开,求若干个一次函数的积分即可。
代码贺的。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1005, P = 998244353;
int qpow(int a, int b) {
int ans = 1;
while (b) {
if (b & 1) ans = 1ll * ans * a % P;
a = 1ll * a * a % P;
b >>= 1;
}
return ans;
}
int n, x[MAXN];
struct Fraction {
int a, b;
Fraction(int x = 0, int y = 1) : a(x), b(y) {
int g = __gcd(a, b);
a /= g, b /= g;
}
bool operator<(const Fraction &x) const {
return 1ll * a * x.b < 1ll * b * x.a;
}
bool operator<=(const Fraction &x) const {
return !(x < *this);
}
operator int() {
return 1ll * a * qpow(b, P - 2) % P;
}
};
map<Fraction, int> mp;
int integral(int k, int b, int l, int r) {
k = (k % P + P) % P;
b = (b % P + P) % P;
return (
(P + 1ll) / 2 * k % P * r % P * r % P + 1ll * b * r % P -
(P + 1ll) / 2 * k % P * l % P * l % P - 1ll * b * l % P + 2 * P
) % P;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &x[i]);
}
x[n + 1] = x[1];
for (int i = 1; i <= n; i++) {
if (x[i] < x[i + 1]) {
for (int j = 1; j < x[i + 1] - x[i]; j++) {
mp[{j, x[i + 1] - x[i]}]--;
}
} else {
for (int j = 0; j < x[i] - x[i + 1]; j++) {
mp[{j, x[i] - x[i + 1]}]++;
}
}
}
mp[{0, 1}], mp[{1, 1}];
int y = 0, b = 0;
int ans = 0;
vector<pair<Fraction, int>> v;
for (auto p : mp) {
v.push_back(p);
}
for (int i = 0; i + 1 < v.size(); i++) {
y += v[i].second;
while (Fraction(b + 1, abs(x[1] - x[n])) <= v[i].first) b++;
if (y == 1) {
ans = (ans + integral(x[1] - x[n], x[1] > x[n] ? -b : b + 1, v[i].first, v[i + 1].first)) % P;
}
}
ans = 1ll * ans * qpow(3, P - 2) % P;
printf("%d\n", ans);
return 0;
}