do_while_true

一言(ヒトコト)

「题解」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;
}
posted @ 2021-06-30 18:56  do_while_true  阅读(25)  评论(0编辑  收藏  举报