题解: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\) 操作后,最终排列的逆序对数就是
我们要对所有 \(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}\),套路地把绝对值拆掉:
以
为例。先把 \(a\) 吸收掉,得到
反转一下,令 \(b\gets R_i-b\),那么限制变为 \(R_i-b\leq a+1\Leftrightarrow a+b\geq R_i-1\)。我们枚举 \(s=a+b\),内层再用下指标卷积化简:
这样我们就把式子化成了组合数下指标前缀和的形式。另外三项类似处理。
现在问题转化为 \(\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;
}

浙公网安备 33010602011771号