首页 > 其他分享 >[ABC337G] Tree Inversion(换根 dp + 值域线段树)

[ABC337G] Tree Inversion(换根 dp + 值域线段树)

时间:2024-10-21 21:49:37浏览次数:7  
标签:Inversion re int sum Tree mid fa query ABC337G

link

题目形式就很换根 dp

如果这种题用朴素的做法求,就是暴力以每个点都做一次根跑树,自底向上统计,时间是 \(O(n^2)\)

而换根 dp 的思想就是分两步,

一般先钦定某个点(如 1)为根,统计一遍以 1 为根时的结果,

然后挖掘如果以其他点为根时,变换对结果的影响,一般就是自顶向下更新如果换根后的信息

这样分成两次 dfs,时间就是线性的 \(O(n)\)


这个题发现就是求类似树上逆序对的个数,即在同一个子树里,节点编号和深度大小关系相反

考虑第一步,设 \(f[x]\) 表示以节点编号 \(x\) 为根的子树包含逆序对个数

显然,我们要知道子树中编号小于 \(x\) 的节点数量 \(a_x\),那么转移就很显然了:

\[f[x] = a_x + \sum\limits_{y\in son(x)}f[y] \]


考虑第二步,设 \(g[x]\) 表示以 \(x\) 为根节点的树的逆序对个数

思考由 \(x\) 换根为 \(y\) 后,\(g[x]\) 通过怎样的转换关系得到 \(g[y]\)

image

这里还需要增加一个 \(b_x\) 表示以 \(x\) 为根的子树内编号小于 \(fa[x]\) 的节点数量

需要注意的是,总的小于 \(y\) 的节点个数是 \(y - 1\),已知 \(y\) 子树内的数量有 \(a_y\),相减即子树外的部分

\[g[y] = g[x] - b_y + (y - 1 - a_y) \]


对于 \(a_x\),\(a_y\) 的求解

也是很重要的一部分,这里是很经典的建值域线段树,或是树状数组,只不过这题是在树上

要求子树 \(x\) 内的值,可以作差一下,用全局的值减掉子树外的值即可,对应的就是递归完子树和递归子树前

时间是 \(O(n\log n)\) 的

#include <bits/stdc++.h>
#define re register int 
#define lp p << 1
#define rp p << 1 | 1
#define int long long

using namespace std;
const int N = 2e5 + 10;

struct Edge
{
	int to, next;
}e[N << 1];
int idx, h[N];
struct Tree
{
	int l, r, sum;
}t[N << 2];
int n, f[N], g[N], a[N], b[N];

inline void add(int x, int y)
{
	e[++ idx] = (Edge){y, h[x]};
	h[x] = idx;
}

inline void build(int p, int l, int r)
{
	t[p].l = l, t[p].r = r;
	if (l == r) return;
	int mid = (l + r) >> 1;
	build(lp, l, mid);
	build(rp, mid + 1, r);
}

inline void update(int p, int x, int k)
{
	if (t[p].l == x && t[p].r == x)
	{
		t[p].sum += k;
		return;
	}
	int mid = (t[p].l + t[p].r) >> 1;
	if (x <= mid) update(lp, x, k);
	if (x > mid) update(rp, x, k);
	t[p].sum = t[lp].sum + t[rp].sum;
}

inline int query(int p, int l, int r)
{
	if (l > r) return 0;
	if (l <= t[p].l && t[p].r <= r) return t[p].sum;
	int res = 0;
	int mid = (t[p].l + t[p].r) >> 1;
	if (l <= mid) res += query(lp, l, r);
	if (r > mid) res += query(rp, l, r);
	
	return res;	
}

void dfs1(int u, int fa)
{
	a[u] = -query(1, 1, u - 1);
	b[u] = -query(1, 1, fa - 1);
	
	for (re i = h[u]; i; i = e[i].next) 
	{
		int v = e[i].to;
		if (v == fa) continue;
		dfs1(v, u);
	}
	update(1, u, 1);
	a[u] += query(1, 1, u - 1);
	b[u] += query(1, 1, fa - 1);
	
	f[u] = a[u];
	for (re i = h[u]; i; i = e[i].next)
	{
		int v = e[i].to;
		if (v == fa) continue;
		f[u] += f[v]; 
	}
}

void dfs2(int u, int fa)
{
	if (u != 1)
		g[u] = g[fa] - b[u] + (u - 1 - a[u]);
	for (re i = h[u]; i; i = e[i].next)
	{
		int v = e[i].to;
		if (v == fa) continue;
		dfs2(v, u);
	}
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n;
	for (re i = 1; i < n; i ++)
	{
		int x, y; cin >> x >> y;
		add(x, y), add(y, x);
	}
	build(1, 1, n);
	dfs1(1, 0);
	g[1] = f[1];
	dfs2(1, 0);
	for (re i = 1; i <= n; i ++) cout << g[i] << ' '; 
	cout << '\n';
	
	return 0;
}

标签:Inversion,re,int,sum,Tree,mid,fa,query,ABC337G
From: https://www.cnblogs.com/wenzieee/p/18490432

相关文章

  • 三,TreeMap和HashMap,TreeSet和HashMap的区别以及方法使用上的不同
    TreeMap和HashMap的区别TreeMap:基于红黑树实现。提供了范围查询和排序功能。所有操作的时间复杂度为O(logn)。不允许键为null。键必须实现Comparable接口或提供一个Comparator。HashMap:基于哈希表实现。提供快速的查找、插入和删除操作。平均时间复杂度为O(1),......
  • 闯关leetcode——110. Balanced Binary Tree
    大纲题目地址内容解题代码地址题目地址https://leetcode.com/problems/balanced-binary-tree/description/内容Givenabinarytree,determineifitisheight-balanced.Aheight-balancedbinarytreeisabinarytreeinwhichthedepthofthetwosub......
  • [ARC185D] Random Walk on Tree 题解
    一个很套路的做法。思路题目要求走完整个树的时间,这并不好算,容易想到min-max容斥。依据min-max容斥,我们可以轻松把它转化成第一次走到所有子集的时间。考虑在这道题中,有什么特殊的。第一,任何包含根节点的子集答案都是零。第二,由于我们只关心第一次走到的点的时间,因此假......
  • 最小生成树(Minimum Spanning Tree,MST)初步
    定义连通图的最小生成树(MinimumSpanningTree,MST)为边权和最小的生成树。注意:只有连通图才有生成树,而对于非连通图,只存在生成森林。思路分为Kruskal与Prim两种算法。Kruskal从最小边权的边开始,按边权从小到大依次遍历。若当前边连接的两点不连通,加入此边。Prim每次选......
  • java_day14_HashSet、TreeSet、增强for循环、Map、HashMap、TreeMap、可变参数
    一、HashSetSet:HashSet:底层数据结构是哈希表,查找速度快,且元素唯一HashSet中的add方法实际上调用的是HashMap中的put方法底层和元素的hashCode方法值有关我们发现,底层判断待插入的元素是否已经存在哈希表中的方式是:将待插入的元素的哈希值与已经存......
  • AntDesign树形组件tree和输入input组件融合使用
    <a-tree :tree-data="selectItem.options.options" :replace-fields="{ children:'children', title:'label', code:'code' }" >......
  • Map集合中的具体子类TreeMap
    一、TreeMap元素是一个键值对,可以去重并进行排序1.先编写一个Dog2类publicclassDog2{privateStringname;privateintage;publicDog2(){}publicDog2(Stringname,intage){this.name=name;......
  • [题解]NOIP2018模拟赛 plutotree
    题目描述给定一棵有\(n\)个节点的树,根节点为\(1\),节点\(i\)有权值\(w[i]\)。这棵树非常奇怪,它的每个叶子结点都有一条连向根节点的权值为\(0\)的边。给定\(q\)次询问,每次给定\(u,v\),请计算出一条\(u\)到\(v\)的路径(每条边最多经过\(1\)次),最小化该路径上的点权之和,并在其基础上最......
  • Set集合的直接子类TreeSet
    一、TreeSet:底层数据结构是红黑树(自平衡二叉树),具备了可预测的排序1.自然排序通过实现comparable接口,重写里面的compareTo方法来进行排序1.编写一个Dog类,实现了Comparable接口,并重写里面的方法publicclassDogimplementsComparable<Dog>{privateStringname;pri......
  • Sourcetree打不开、无法启动、闪退解决方法
    不知道你们有没有出现过突然Sourcetree软件打不开了,打开的时候只出现弹框一两秒。后来发现只要删除一个文件即可恢复路径查找找到删除的文件就可以直接运行了C:\Users\admin\AppData\Local\Atlassian 顺便告诉大家一个快捷找到删除文件的路径方式1、右击桌面图标,选择打开......