树的旋转「POI 2011」

【题目描述】

现在有一棵二叉树,所有非叶子节点都有两个孩子。在每个叶子节点上有一个权值(有\(n\)个叶子节点,满足这些权值为\(1\dots n\)的一个排列)。可以任意交换每个非叶子节点的左右孩子。

要求进行一系列交换,使得最终所有叶子节点的权值按照遍历序写出来,逆序对个数最少。

【输入格式】

第一行n下面每行,一个数x

如果\(x=0\),表示这个节点非叶子节点,递归地向下读入其左孩子和右孩子的信息,

如果\(x\ne 0\),表示这个节点是叶子节点,权值为\(x\)

【输出格式】

一行,最少逆序对个数。

题解

此题输入稍微有点毒瘤啊。。。但是反正是按照dfs序给的 就边dfs边读入好了

如果我们把所有的叶子节点从左到右编号为\(1\sim n\),那么某个节点\(x\)的子树中必定含有编号连续的一段叶子节点\([l,r]\),也就是说叶子节点\(l\)到叶子节点\(r\)都在\(x\)的子树里

方便起见我们再把叶子节点\(i\)的权值定义为\(v_i\)

现在考虑一个非叶子节点\(x\) 它的左儿子是\(a\),右儿子是\(b\) 我们记\(a\)子树里含有\([l_a, r_a]\)的叶子节点,\(b\)子树里有\([l_b, r_b]\)的叶子节点

我们让\(f(x)\)等于 满足\(l_a\le i\le r_a,\ l_b\le j\le r_b,\ v_i>v_j\)的所有逆序对\((i,j)\)的数量 那么如果没有交换操作的话 答案就是所有的\(f(i)\)之和

这个我也不知道怎么解释。。。似乎挺显然的 因为左右儿子内部的逆序对之前已经统计完了嘛

那么如果我们交换了\(a,b\)的位置 \(f(x)\)会有什么变化呢

\(f(x)\)就会变成满足\(l_a\le i\le r_a,\ l_b\le j\le r_b,\ v_i<v_j\)的\((i,j)\)的数量

因为左右交换了 所以原来的顺序对全部变成了逆序对 逆序对全部变成了顺序对

怎么求逆序对?

因为此题给出的二叉树不一定是完全二叉树 所以不能用归并排序 树状数组则不方便我们快速统计上面要求的那个东西,也不好合并 所以我们用权值线段树

我们要求的是这个:满足\(l_a\le i\le r_a,\ l_b\le j\le r_b,\ v_i>v_j\)的所有\((i,j)\)的数量 这个可以在合并左儿子和右儿子的线段树时顺便统计出来

我们依然把左儿子叫做\(a\),右儿子叫做\(b\)

具体来说 对于权值线段树的一个节点代表的区间\([l,r]\) 令\(cnt1\)等于\(a\)的线段树在\([mid+1,r]\)区间的元素个数(就是说左儿子的子树里的叶子节点\(i\) 有多少个\(mid+1\le v_i\le r\)) \(cnt2\)等于\(b\)的线段树在\([l,mid]\)区间的元素个数

那么\(cnt1\)的节点和\(cnt2\)的节点两两匹配都一定满足\(l_a\le i\le r_a,\ l_b\le j\le r_b,\ v_i>v_j\) 所以\(f(x)\)加上\(cnt1*cnt2\)

对于权值线段树上每个节点都算一次

注意 这样统计一定是不重不漏的 很好理解 但是我并不知道怎么解释。。。

反过来也同理

因为你要么交换 要么不交换 所以答案就加上两种\(f(x)\)中较小的那一个

这篇题解真难写

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int n, m, son[1000005][2], out[1000005];

struct segtree{
	int lc, rc, cnt;
} tr[4000005];
int tot;

int rt[1000005];
ll ans;

//权值线段树 ↓

void update(int &ind, int l, int r, int p) {
	if (!ind) ind = ++tot;
	tr[ind].cnt++;
	if (l == r) return;
	int mid = (l + r) >> 1;
	if (p <= mid) update(tr[ind].lc, l, mid, p);
	else update(tr[ind].rc, mid+1, r, p);
}

int query(int ind, int l, int r, int x, int y) {
	if (x > y || !ind) return 0;
	if (x <= l && r <= y) return tr[ind].cnt;
	int mid = (l + r) >> 1, ret = 0;
	if (x <= mid) ret += query(tr[ind].lc, l, mid, x, y);
	if (mid < y) ret += query(tr[ind].rc, mid+1, r, x, y);
	return ret;
}

//权值线段树 ↑

int merge(int x, int y, ll &mn, ll &mx) {
	if (!x) return y;
	if (!y) return x;
	mn += 1ll * tr[tr[x].lc].cnt * tr[tr[y].rc].cnt;
	mx += 1ll * tr[tr[y].lc].cnt * tr[tr[x].rc].cnt;
	tr[x].cnt += tr[y].cnt;
	tr[x].lc = merge(tr[x].lc, tr[y].lc, mn, mx);
	tr[x].rc = merge(tr[x].rc, tr[y].rc, mn, mx);
	return x;
}

int dfs() {
	int ind = ++m;
	int val; scanf("%d", &val);
	if (val) {
		update(rt[ind], 1, n, val);
	} else {
		int lc = dfs(), rc = dfs();
		ll mn = 0, mx = 0;
		rt[ind] = merge(rt[lc], rt[rc], mn, mx);
		ans += min(mn, mx);
	}
	return ind;
}

int main() {
	scanf("%d", &n);
	dfs();
	printf("%lld\n", ans);
	return 0;
}

原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM63.html

时间: 2024-08-06 16:48:41

树的旋转「POI 2011」的相关文章

「POI 2005」SZA-Template 「失配树」「双向链表」「思维」

先来观察答案的几个强性质. 首先答案肯定是原串的一个\(\tt{border}\),也就是失配树上的一条链. 再进一步观察:比如说答案在原串出现的位置分别为\(p_1, p_2, p_3... p_k\)(不妨设其严格升序),那么一定有\(\max (p_i - p_{i-1}) \leq length(ans)\). 你问我为什么?如果大于的话就接不上了啊... 然后我们发现,只要满足上面那两个条件,那这个串一定就是个合法串.于是我们把这套东西搬到失配树上: 这个串是n在树上的祖先 这个串在树

染色「SDOI 2011」

[题目描述] 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如"112221"由3段组成:"11"."222"和"1". 请你写一个程序依次完成这m个操作. [输入格式] 第一行包含2个整数n和m,分别表示节点数和操作数: 第二行包含n个正整数表示n个节点的初始颜色 下面n-1行每行包含两个整数x和y,

[Luogu 3701] 「伪模板」主席树

[Luogu 3701] 「伪模板」主席树 <题目链接> 这是一道网络流,不是主席树,不是什么数据结构,而是网络流. 题目背景及描述都非常的暴力,以至于 Capella 在做此题的过程中不禁感到生命流逝. S 向 byx 的树中的每一个人连有向边,手气君的树中的每一个人向 T 连有向边,边权为这个人的寿命.统计同一棵树中的膜法师数量 x.如果一个人是主席,那么边权要加上 x.(续得好啊) 然后,如果 byx 树中的一个点 i 能赢手气君树中的点 j,那么连 i->j,边权为 1. 跑最大

P3701 「伪模板」主席树

P3701 「伪模板」主席树 题目背景 byx和手气君都非常都非常喜欢种树.有一天,他们得到了两颗奇怪的树种,于是各自取了一颗回家种树,并约定几年后比一比谁种出来的树更加牛x. 题目描述 很快,这棵树就开花结果了.byx和手气君惊讶的发现,这是一棵主席树,树上长满了主席和主席的朋友们.这棵树上一共有五种人,主席(J),记者(HK),高人(W),女王(E)和膜法师(YYY).他们发现,他们的主席树上的人数相同,都为N. 研究发现,这五种人的输赢如上图所示(一样的人不能PK),箭头指向输的人.至于为

「不会」矩阵树定理

定理不会证,也不会用 「小z的房间」 暴力建图 「重建」 矩阵树求的是$\sum\limits_{T} \prod\limits_{e\in T} w(e)$ 而要求的是$\sum\limits_{T} \prod\limits_{e\in T}p(e)*\prod\limits_{e isnot\in T} 1-p(e)$ 即$\prod\limits_e 1-p(e) \sum\limits_{T} \prod\limits_{e\in T}p(e)/(1-p(e))$ 设$w(e)=p(e

OpenSSL 爆新漏洞「中间人攻击」

今年 4 月 8 日,OpenSSL 曾曝出严重的安全漏洞「心脏出血」.它使攻击者能够从内存中读取多达 64 KB 的数据,从而可以实时获取到以 https 开头网址的用户的重要信息,包括登录账号和密码等. 如今「心脏出血」还未被完全修复,又出现了新的漏洞.本周 OpenSSL 紧急通知用户,发现一个名为「中间人攻击」(man-in-the-middle attack) 的漏洞,黑客可以利用这一漏洞截获并读取用户隐私信息. OpenSSL 心脏出血 中间人攻击 安全漏洞 OpenSSL漏洞 「中

Netflix 不想交「苹果税」,新用户不能在 iOS 端付费了

新上线的电影<黑镜>的开放式结局让 Netflix 又一次刷新了观众的观影体验,而这家已经成为流媒体行业标杆的公司也有了更多的底气拒绝「苹果税」,他们宣布今后新用户将无法从 iOS 渠道付费,建议从网页端订阅服务. 「我们不再支持 iTunes 作为新用户的订阅方式,」一位 Netflix 的发言人向 VentureBeat 证实,不过他补充说老用户仍可在 iOS 端进行应用内购买. Netflix 没有透露这一变化具体是何时上线的,但平台客服表示大概是从上个月底开始的,此外,客服人员还确认老

Loj #3085. 「GXOI / GZOI2019」特技飞行

Loj #3085. 「GXOI / GZOI2019」特技飞行 题目描述 公元 \(9012\) 年,Z 市的航空基地计划举行一场特技飞行表演.表演的场地可以看作一个二维平面直角坐标系,其中横坐标代表着水平位置,纵坐标代表着飞行高度. 在最初的计划中,这 \(n\) 架飞机首先会飞行到起点 \(x = x_{st}\) 处,其中第 \(i\) 架飞机在起点处的高度为 \(y_{i,0}\).它们的目标是终点 \(x = x_{ed}\) 处,其中第 \(i\) 架飞机在终点处的高度应为 \(y

loj #3146. 「APIO 2019」路灯

loj #3146. 「APIO 2019」路灯 暴力的话就是查询\((l,r)\)之间是否全部是1,考虑如何优化查询 我们可以利用\(set\)来维护每一个全\(1\)区间和它出现的时间,具体的,用\((lp,rp,l,r)\)来表示\((lp,rp)\)的全\(1\)区间在时间\([l,r]\)中是存在的 那么对于一个在时间\(i\)的询问\((l_i,r_i)\),\((lp,rp,l,r)\)会对它产生贡献当且仅当\(lp\leq l_i,rp\geq r_i,i\geq l\),产生的