arc228C Partially Sort
给定长为 \(n\) 的排列 \(a\)。对于所有 \(2^n\) 种 \([1,n]\) 的子序列 \(T\),将 \(a\) 中位于 \(T\) 中的数从小到大排序后放回原位后得到序列 \(b\)。
试计数所有 \(2^n\) 种 \(b\) 的逆序对之和。
考虑原序列中的一对数 \((a_i,a_j)\)。若 \(i,j\) 均 \(\in T\),则其必不贡献逆序对。若 \(i,j\) 均 \(\notin T\),则其在原序列上为逆序对时贡献 \(2^{n-2}\)。
那么考虑仅有其中之一 \(\in T\) 的情况。不妨枚举 \(x\notin T\)。记:
(即下标和值域上在子序列中的排名
对于 \(y\in T\),同理记 \(r_y\)。由于子序列 \(T\) 排序,则 \(y\) 排序后在子序列中的位置即为 \(r_y\),那么 \((x,y)\) 贡献逆序对当且仅当 \(r_x<r_y\le p_x\lor p_x< r_y\le r_x\)。
那么所有 \(y\) 贡献 \(|r_x-p_x|\) 次逆序对,记 \(f(x)=r_x-p_x\)。

如上图,竖轴为值域,横轴为下表。
左侧的数对 \(f(x)\) 贡献 \(-1\),下侧的数对 \(f(x)\) 贡献 \(1\)。那么左下和右上的数(红色)对 \(f(x)\) 贡献 \(0\)。左上(蓝)贡献 \(-1\),右下(绿)贡献 \(-1\)。
设蓝色部分有 \(a\) 个数,绿色部分 \(b\) 个。则 \(n-a-b-1\) 个红色数。考虑对所有 \(T\) 下的 \(f(x)\) 计数:
前后两项本质相同,考虑前半部分:
也即我们需要处理如下求和:
组合数下指标求和有经典 trick:考虑莫队。对于状态 \((n,m,A)\),其中 \(A\) 为上述求和的答案。
- 转移到 \(m-1\) / \(m+1\) 是简单的。
- 转移到 \(n+1\) 考虑帕斯卡递推:\(\binom nm=\binom{n-1}{m-1}+\binom{n-1}m\),立即有 \(A'=2A-\binom nm\)。
- 同理转移到 \(n-1\) 有:\(A'=\frac 12(n+\binom{n-1}m)\)。
那么即可在 \(\mathcal O(n\sqrt n)\) 时间内解决。
#include <algorithm>
#include <iostream>
#include <string.h>
#include <vector>
#include <math.h>
const int N = 5e5 + 7, O = 998244353;
typedef long long i64;
inline i64 fpow(i64 x, i64 k) {
i64 res = 1;
for(; k; k >>= 1, x = x * x %O)
if(k & 1) res = res * x %O;
return res;
}
#define inv(x) fpow((x),O-2)
int n, v[N];
i64 frac[N+1], ifac[N+1], p2[N+1];
inline void init() {
frac[0] = 1;
for(int i = 1; i <= N; ++i) frac[i] = frac[i-1] * i %O;
ifac[N] = inv(frac[N]);
for(int i = N; i >= 1; --i) ifac[i-1] = ifac[i] * i %O;
p2[0] = 1;
for(int i = 1; i <= N; ++i) p2[i] = p2[i-1] * 2 %O;
}
inline i64 C(int n, int m) {
if(m > n || n < 0 || m < 0) return 0;
return frac[n] * ifac[m] %O * ifac[n-m] %O;
}
struct BIT {
int f[N];
void add(int x) {
for(; x < N; x += x & -x)
++f[x];
}
int get(int x) {
int y = 0;
for(; x; x -= x & -x)
y += f[x];
return y;
}
void clear() {
memset(f, 0, sizeof f);
}
} T;
int w1[N], w2[N];
inline void cdq(int *w) {
T.clear();
for(int i = 1; i <= n; ++i)
w[i] = T.get(v[i]), T.add(v[i]);
}
struct Qry { int x, y, coef; } q[N]; int m;
auto incr = [](auto& x, auto&& y) { x += y, x -= (x >= O) *O; };
int main() {
init();
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
std::cin >> n; for(int i = 1; i <= n; ++i) std::cin >> v[i];
std::reverse(v + 1, v + n + 1);
cdq(w2);
for(int i = 1; i <= n; ++i) v[i] = n - v[i] + 1;
std::reverse(w2 + 1, w2 + n + 1);
std::reverse(v + 1, v + n + 1);
cdq(w1);
i64 s1 = 0, ans = 0;
for(int i = 1; i <= n; ++i) incr(s1, w1[i]);
incr(ans, s1 * p2[n-2] %O);
auto calc = [&](int a, int b, i64 coef) {
if(a > b) std::swap(a, b);
incr(ans, (b - a) * p2[a + b - 1] %O * coef %O);
if(a >= 1)
incr(ans, 2 * a * C(a + b - 1, a - 1) %O * coef %O);
if(a >= 2 && b > a)
q[++m] = {a + b - 1, a - 2, O - int(2ll * (b - a) * coef %O)};
};
for(int i = 1; i <= n; ++i) calc(w1[i], w2[i], p2[n-w1[i]-w2[i]-1]);
int k = sqrt(n);
static int id[N]; for(int i = 1; i <= n; ++i) id[i] = (i - 1) / k + 1;
std::sort(q + 1, q + m + 1, [&](auto&& x, auto&& y) {
return id[x.x] == id[y.x] ? id[x.x] & 1 ? x.y < y.y : x.y > y.y : x.x < y.x;
});
const i64 inv2 = inv(2);
int a = 0, b = 0; i64 c = 1;
for(int i = 1; i <= m; ++i) {
auto& [x, y, coef] = q[i];
while(b < y) incr(c, C(a, ++b));
while(b > y) incr(c, O-C(a, b--));
while(a < x) c = (O + 2 * c - C(a++, b)) %O;
while(a > x) c = inv2 * (c + C(--a, b)) %O;
incr(ans, 1ll * coef * c %O);
}
std::cout << ans << "\n";
}
本文来自博客园,作者:CuteNess,转载请注明原文链接:https://www.cnblogs.com/CuteNess/p/22811326

浙公网安备 33010602011771号