「题解」LgP3521 [POI2011]ROT-Tree Rotations
注意到如果我们用类似 cdq 分治的思想,对于每个节点统计“左子树的叶子和右子树的叶子进行匹配的逆序对数”从而求和得到总体逆序对数,那么对于任意两个不同节点,他们各自的两个子树交不交换位置对答案的影响是互相独立的。
所以只需要求对于每个节点是否交换优即可。
考虑在每个叶节点开权值线段树然后向上合并,合并的过程中我们就能以同样类似 cdq 分治思想的做法,把 逆序对/顺序对 求出来,取最小值加入到答案中即可。
具体求法是,线段树合并的从中间断开递归的时候,算出左区间和右区间匹配出的逆序对,然后再递归下去的时候算左区间和右区间内部的逆序对。
时间复杂度 \(\mathcal{O}(n\log n)\)
#include<iostream>
#include<cstdio>
#include<algorithm>
typedef long long ll;
template <typename T> T Max(T x, T y) { return x > y ? x : y; }
template <typename T> T Min(T x, T y) { return x < y ? x : y; }
template <typename T>
T &read(T &r) {
r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
while(ch >= '0' && ch <= '9') r = (r << 3) + (r <<1) + (ch ^ 48), ch = getchar();
return r = w ? -r : r;
}
#define ls(x) tree[x].lson
#define rs(x) tree[x].rson
const int N = 3.8e6;
const int INF = 0x7fffffff;
int n, trnt;
struct SGT {
int lson, rson, sum;
}tree[N];
void modify(int &x, int tl, int tr, int pos) {
if(!x)
x = ++trnt;
++tree[x].sum;
if(tl == tr) return ;
int mid = (tl + tr) >> 1;
if(pos <= mid) modify(ls(x), tl, mid, pos);
else modify(rs(x), mid+1, tr, pos);
}
ll s1, s2, ans;
void merge(int &x, int y, int tl, int tr) {
if(!x || !y) {
x = x + y;
return ;
}
tree[x].sum += tree[y].sum;
if(tl == tr) return ;
s1 += 1ll * tree[rs(x)].sum * tree[ls(y)].sum;
s2 += 1ll * tree[rs(y)].sum * tree[ls(x)].sum;
int mid = (tl + tr) >> 1;
merge(ls(x), ls(y), tl, mid);
merge(rs(x), rs(y), mid+1, tr);
}
void dfs(int &x) {
int t; read(t);
if(!t) {
int lson = 0, rson = 0;
dfs(lson);
dfs(rson);
s1 = 0; s2 = 0;
x = lson; merge(x, rson, 1, n);
ans += Min(s1, s2);
}
else modify(x, 1, n, t);
}
#undef ls
#undef rs
signed main() {
// freopen("in.txt", "r", stdin);
read(n); int root = 0;
dfs(root);
printf("%lld\n", ans);
return 0;
}