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;
}