题解:ARC228C Partially Sort

哎哟我怎么不会做推式子题,火大了。

考虑固定一个未被选中的位置 \(k\),对于一个子序列 \(S\),设 \(x=\sum_{i\in S}[i<k]\)\(y=\sum_{i\in S}[p_i<p_k]\),则 \(k\) 会与 \(S\) 中位置会构成 \(|x-y|\) 个逆序对。再设 \(l=\sum_{j\in S}[j<i\land p_j>p_i]\)\(r=\sum_{j\in S}[i<j\land p_i>p_j]\),不难推出 \(|x-y|=|l-r|\)

那么对一个 \(S\) 操作后,最终排列的逆序对数就是

\[\sum_{x,y\in S}[x<y\land p_x>p_y]+\sum_{i\in S}|l-r| \]

我们要对所有 \(S\) 求上面的式子的总和。

对于第一个部分,考察一个原排列中的逆序对 \((i,j)\),有 \(2^{n-2}\)\(S\) 可以覆盖这个逆序对,因此这部分的贡献就是 \(2^{n-2}C\),其中 \(C\) 表示 \(p\) 中的逆序对数。

对于第二个部分,固定一个 \(i\in S\),设 \(L_i=\sum_{j=1}^{i-1}[p_j>p_i]\)\(R_i=\sum_{j=i+1}^n[p_i>p_j]\),不妨枚举 \(S\) 从左边的 \(L_i\) 个元素中选择了 \(a\) 个,从右边的 \(R_i\) 个元素中选择了 \(b\) 个,剩下的 \(n-1-L_i-R_i\) 个元素可以任意选择,因此贡献为

\[2^{n-1-L_i-R_i}\sum_{a=0}^{L_i}\sum_{b=0}^{R_i}\binom{L_i}{a}\binom{R_i}{b}|a-b| \]

暂时略去 \(2^{n-1-L_i-R_i}\),套路地把绝对值拆掉:

\[\begin{align*} &\sum_{a=0}^{L_i}\sum_{b=0}^{R_i}[b\leq a]\binom{L_i}{a}\binom{R_i}{b}(a-b)+\sum_{a=0}^{L_i}\sum_{b=0}^{R_i}[b\geq a+1]\binom{L_i}{a}\binom{R_i}{b}(b-a)\\ =&\sum_{a=0}^{L_i}\sum_{b=0}^{R_i}[b\leq a]\binom{L_i}{a}\binom{R_i}{b}a-\sum_{a=0}^{L_i}\sum_{b=0}^{R_i}[b\leq a]\binom{L_i}{a}\binom{R_i}{b}b\\ +&\sum_{a=0}^{L_i}\sum_{b=0}^{R_i}[b\geq a+1]\binom{L_i}{a}\binom{R_i}{b}b-\sum_{a=0}^{L_i}\sum_{b=0}^{R_i}[b\geq a+1]\binom{L_i}{a}\binom{R_i}{b}a \end{align*} \]

\[\sum_{a=0}^{L_i}\sum_{b=0}^{R_i}[b\leq a]\binom{L_i}{a}\binom{R_i}{b}a \]

为例。先把 \(a\) 吸收掉,得到

\[\begin{align*} &L_i\sum_{a=1}^{L_i}\sum_{b=0}^{R_i}[b\leq a]\binom{L_i-1}{a-1}\binom{R_i}{b}\\ =&L_i\sum_{a=0}^{L_i-1}\sum_{b=0}^{R_i}[b\leq a+1]\binom{L_i-1}{a}\binom{R_i}{b} \end{align*} \]

反转一下,令 \(b\gets R_i-b\),那么限制变为 \(R_i-b\leq a+1\Leftrightarrow a+b\geq R_i-1\)。我们枚举 \(s=a+b\),内层再用下指标卷积化简:

\[\begin{align*} &L_i\sum_{a=0}^{L_i-1}\sum_{b=0}^{R_i}[a+b\geq R_i-1]\binom{L_i-1}{a}\binom{R_i}{b}\\ =&L_i\sum_{s=R_i-1}^{L_i+R_i-1}\sum_{\substack{0\leq a\leq L_i-1\\0\leq b\leq R_i\\a+b=s}}\binom{L_i-1}{a}\binom{R_i}{b}\\ =&L_i\sum_{s=R_i-1}^{L_i+R_i-1}\binom{L_i+R_i-1}{s}\\ =&L_i\sum_{s=0}^{L_i}\binom{L_i+R_i-1}{s} \end{align*} \]

这样我们就把式子化成了组合数下指标前缀和的形式。另外三项类似处理。

现在问题转化为 \(\mathcal{O}(n)\) 次查询组合数下指标前缀和。这是经典问题,莫队即可 \(\mathcal{O}(n\sqrt{n})\) 维护。

\(L_i,R_i,C\) 可以用树状数组 \(\mathcal{O}(n\log{n})\) 求出。

时间复杂度为 \(\mathcal{O}(n\sqrt{n}+n\log{n})\)

主要代码
int n, p[MAXN], L[MAXN], R[MAXN];
int blLen, lb[MAX_CNT], rb[MAX_CNT], blNum[MAXN];
mint inv2 = mint(2).inv();
mint fac[MAXN << 1], ifac[MAXN << 1], pw2[MAXN];
mint cnt, d[MAXN];

struct Query {
	int id, c, l, r;

	friend bool operator<(const Query &lhs, const Query &rhs) {
		if (blNum[lhs.l] != blNum[rhs.l]) return blNum[lhs.l] < blNum[rhs.l];
		else return blNum[lhs.l] & 1 ? lhs.r < rhs.r : lhs.r > rhs.r;
	}
};

struct BIT {
	int c[MAXN];

	void init() {
		fill(c + 1, c + n + 1, 0);
	}

	int query(int x) {
		int res = 0;
		for (; x; x -= lowbit(x)) res += c[x];
		return res;
	}

	void add(int x, int v) {
		for (; x <= n; x += lowbit(x)) c[x] += v;
	}
} ft;

mint qpow(mint a, ll b) {
	mint res = 1;
	for (; b; b >>= 1) {
		if (b & 1) res *= a;
		a *= a;
	}
	return res;
}

void init(int n) {
	fac[0] = 1;
	for (int i = 1; i <= n; ++i) fac[i] = fac[i - 1] * i;
	ifac[n] = fac[n].inv();
	for (int i = n - 1; i >= 0; --i) ifac[i] = ifac[i + 1] * (i + 1);
}

mint C(int n, int m) {
	return n < 0 || m < 0 || n < m ? 0 : fac[n] * ifac[m] * ifac[n - m];
}

void solve(const vector<Query> &queries) {
	int L = 1, R = 0;
	mint cur = 1;
	for (auto [id, c, l, r] : queries) {
		while (R < r) cur += C(L, ++R);
		while (L > l) cur = (cur + C(--L, R)) * inv2;
		while (R > r) cur -= C(L, R--);
		while (L < l) cur = cur * 2 - C(L++, R);
		d[id] += cur * c;
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	cin >> n;
	for (int i = 1; i <= n; ++i) cin >> p[i];

	init(n * 2);
	pw2[0] = 1;
	for (int i = 1; i <= n; ++i) pw2[i] = pw2[i - 1] * 2;

	for (int i = 1; i <= n; ++i) {
		cnt += L[i] = ft.query(n) - ft.query(p[i]);
		ft.add(p[i], 1);
	}

	ft.init();
	for (int i = n; i; --i) {
		R[i] = ft.query(p[i] - 1);
		ft.add(p[i], 1);
	}

	blLen = sqrt(n);
	for (int i = 1, j = 1; j <= n; ++i) {
		lb[i] = j;
		rb[i] = min(j + blLen - 1, n);
		while (j <= rb[i]) blNum[j++] = i;
	}

	vector<Query> queries;
	for (int i = 1; i <= n; ++i) {
		queries.push_back({i, L[i], L[i] + R[i] - 1, L[i]});
		if (L[i] >= 1) queries.push_back({i, -R[i], L[i] + R[i] - 1, L[i] - 1});
		if (R[i] >= 1) queries.push_back({i, R[i], L[i] + R[i] - 1, R[i] - 1});
		if (R[i] >= 2) queries.push_back({i, -L[i], L[i] + R[i] - 1, R[i] - 2});
	}
	sort(queries.begin(), queries.end());
	solve(queries);

	mint ans = cnt * pw2[n - 2];
	for (int i = 1; i <= n; ++i) ans += d[i] * pw2[n - 1 - L[i] - R[i]];
	cout << ans;
	return 0;
}
posted @ 2026-08-31 20:24  P2441M  阅读(11)  评论(0)    收藏  举报