arc228C Partially Sort

给定长为 \(n\) 的排列 \(a\)。对于所有 \(2^n\)\([1,n]\) 的子序列 \(T\),将 \(a\) 中位于 \(T\) 中的数从小到大排序后放回原位后得到序列 \(b\)

试计数所有 \(2^n\)\(b\) 的逆序对之和。

\[n \le 2\times 10^5 \]


考虑原序列中的一对数 \((a_i,a_j)\)。若 \(i,j\)\(\in T\),则其必不贡献逆序对。若 \(i,j\)\(\notin T\),则其在原序列上为逆序对时贡献 \(2^{n-2}\)

那么考虑仅有其中之一 \(\in T\) 的情况。不妨枚举 \(x\notin T\)。记:

\[p_x=\sum_{i\in T}[i\le x]\\ r_x=\sum_{i\in T} [a_i\le a_x] \]

(即下标和值域上在子序列中的排名

对于 \(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\)

alt text

如上图,竖轴为值域,横轴为下表。

左侧的数对 \(f(x)\) 贡献 \(-1\),下侧的数对 \(f(x)\) 贡献 \(1\)。那么左下和右上的数(红色)对 \(f(x)\) 贡献 \(0\)。左上(蓝)贡献 \(-1\),右下(绿)贡献 \(-1\)

设蓝色部分有 \(a\) 个数,绿色部分 \(b\) 个。则 \(n-a-b-1\) 个红色数。考虑对所有 \(T\) 下的 \(f(x)\) 计数:

\[\begin{align*} \frac{\text{ans}}{2^{n-a-b-1}}=&\sum f(x) \\=&\sum_i|i|\cdot[z^i](1+z)^a(a+\frac1z)^b \\=&\sum_i|i|\cdot[z^{i+b}](1+z)^{a+b} \\=&\sum_i|i|\binom{a+b}{i+b} \\=&\sum_{i\ge 0}i\binom{a+b}{b+i}+\sum_{i\ge 0}i\binom{a+b}{a+i} \end{align*} \]

前后两项本质相同,考虑前半部分:

\[\begin{align*} \\&\sum_{i\ge0}i\binom{a+b}{b+i} \\=&\sum_{i\le a}(a-i)\binom{a+b}{a-i} \\=&(a+b)\sum_{i\le a}\binom{a+b-1}{a-i-1} \\=&(a+b)\sum_{i\le a-1}\binom{a+b-1}{i} \end{align*} \]

也即我们需要处理如下求和:

\[\sum_{i\le m}\binom ni \]

组合数下指标求和有经典 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";
}
posted @ 2026-09-02 18:08  CuteNess  阅读(3)  评论(0)    收藏  举报