2020 CCPC Wannafly Winter Camp Day1 Div.1&2(A题 期望逆序对)(找规律)
程序员文章站
2022-07-15 16:10:05
...
2020 CCPC Wannafly Winter Camp Day1 Div.1&2(A题 期望逆序对)(找规律)
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
有 个独立的随机变量,其中 的值是一个从 中随机选取的整数,即对于 中的任何一个整数 , 的概率都是 现在你需要给出一个长度为 的排列 ,那么可以得到一个长度为 的随机变量序列 。你的目标是让结果序列的逆序对个数的期望尽可能少。
求逆序对个数的期望的最小值。
输入描述:
第一行输入一个整数 接下来 行每行两个整数 。
输出描述:
输出一行一个整数,表示答案对 取模后的值。假设答案的最简分数表示是 ,你需要输出一个整数 满足 。
示例1
输入
3
1 2
2 3
1 3
输出
332748118
题解
官方题解为:
但我回来反复琢磨后发现一处问题:
这个式子在 的时候处理的不太对,不过题解代码倒是没什么问题。
假如有区间 为 区间 为 ,那么枚举区间 的元素时,当 为 的时候 此时若想让区间 的元素大于区间 的元素,那么无论区间 取哪个,都是不可能的,而官方题解的式子里可以看出区间 里有 中取法(为 ),这显然是不符合的;当 为 的时候,此时区间 里只有一种取法,就是取 ,但是官方题解的式子里得出了 种取法(为 ),这显然也是不符合的。
也就是说当 的时候,每次枚举都会多加 。
修改后的式子:
对 是否大于 进行分类讨论:
令 等于 ,把区间 以 为边界拆成 ,和 两部分(如果 等于 ,那么第二部分为空集),计算第一部分对应的取值情况种类应该有 (等差数列求和,见于代码第33行),计算第二部分对应的取值情况种类应该有 (见于代码第34行)。
代码
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
struct atom {
int l, r;
void scan() {
scanf("%d%d", &l, &r);
}
};
const int N = 5100;
const int mo = 998244353;
int operator < (atom k1, atom k2) {
return k1.l + k1.r < k2.l + k2.r;
}
int n, inv[N];
atom A[N];
int quick(int k1, int k2) {
int k3 = 1;
while (k2) {
if (k2 & 1) k3 = 1ll * k3*k1%mo;
k2 >>= 1; k1 = 1ll * k1*k1%mo;
}
return k3;
}
int calc(atom k1, atom k2) {
int l = max(k1.l, k2.l);
if (l > k1.r) return 0;
int r = min(k1.r, k2.r);
int ans = 1ll * (l - k2.l + r - k2.l)*(r - l + 1) / 2 % mo;
ans = (ans + 1ll * (k1.r - r)*(k2.r - k2.l + 1)) % mo;
return ans;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) A[i].scan();
sort(A + 1, A + n + 1);
int ans = 0;
for (int i = 1; i <= n; i++) inv[i] = quick(A[i].r - A[i].l + 1, mo - 2);
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++)
ans = (ans + 1ll * inv[i] * inv[j] % mo*calc(A[i], A[j])) % mo;
cout << ans << endl;
return 0;
}