CF1009F Dominant Indices 题解

CF1009F Dominant Indices

题面:

题目描述

你得到一个包含 n n n 个顶点的无向树,顶点 1 1 1 是树的根。

我们将顶点 x x x 的深度数组定义为一个无限序列 [ d x , 0 , d x , 1 , d x , 2 , …   ] [d_{x, 0}, d_{x, 1}, d_{x, 2}, \dots] [dx,0,dx,1,dx,2,],其中 d x , i d_{x, i} dx,i 是满足以下两个条件的顶点 y y y 的数量:

x x x y y y 的祖先;
x x x y y y 的简单路径正好经过 i i i 条边。
顶点 x x x 的深度数组的主导索引(简称为顶点 x x x 的主导索引)是一个索引 j j j,满足:

对于每个 k < j k < j k<j d x , k < d x , j d_{x, k} < d_{x, j} dx,k<dx,j
对于每个 k > j k > j k>j d x , k ≤ d x , j d_{x, k} \le d_{x, j} dx,kdx,j
对于树中的每个顶点,计算其主导索引。

输入格式

第一行包含一个整数 n n n 1 ≤ n ≤ 1 0 6 1 \le n \le 10^6 1n106),表示树的顶点数量。

接下来有 n − 1 n-1 n1 行,每行包含两个整数 x x x y y y 1 ≤ x , y ≤ n 1 \le x, y \le n 1x,yn x ≠ y x \ne y x=y),表示树中的一条边。

保证这些边构成一棵树。

输出格式

输出 n n n 个数字,第 i i i 个数字表示顶点 i i i 的主导索引

样例 #1
样例输入 #1
4
1 2
2 3
3 4
样例输出 #1
0
0
0
0
样例 #2
样例输入 #2
4
1 2
1 3
1 4
样例输出 #2
1
0
0
0
样例 #3
样例输入 #3
4
1 2
2 3
2 4
样例输出 #3
2
1
0
0

长链剖分模板题,但是用线段树合并。

这道题长链剖分没看出来,线段树合并一眼!

简单的说,我们要找每个点子树内最厚的深度(哪个深度有最多的点,这里的深度指以该点为根的深度),如果有多个答案,选择最浅的深度作为答案。

我们考虑对于每个点,将每个点的深度加入到该点的权值线段树上,然后将子树的线段树合并到该节点,最后全局查询最左的最大深度。

没什么多说的,上代码。

#include<bits/stdc++.h>
using namespace std;
		
int rd() {
	int x = 0, w = 1;
	char ch = 0;
	while (ch < '0' || ch > '9') {
		if (ch == '-') w = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = x * 10 + (ch - '0');
		ch = getchar();
	}
	return x * w;
}

void wt(int x) {
	static int sta[35];
	int f = 1;
	if(x < 0) f = -1,x *= f;
	int top = 0;
	do {
		sta[top++] = x % 10, x /= 10;
	} while (x);
	if(f == -1) putchar('-');
	while (top) putchar(sta[--top] + 48);
}
const int N = 1e6+5;
int n,dep[N],a[N];
int head[N],nxt[N<<1],to[N<<1],cnt;
void init() {memset(head,-1,sizeof(head));cnt = 0;}
void add(int u,int v) {
	nxt[cnt] = head[u];
	to[cnt] = v;
	head[u] = cnt++;
}

int rt[N];
namespace sgt{
int mx[N * 25],ls[N * 25],rs[N * 25],tot;
#define mid ((pl + pr) >> 1)
void push_up(int p) {mx[p] = max(mx[ls[p]],mx[rs[p]]);}
void update(int &p,int pl,int pr,int k) {
	if(!p) p = ++tot;
	if(pl == pr) {mx[p]++;return;}
	if(k <= mid) update(ls[p],pl,mid,k);
	else update(rs[p],mid+1,pr,k);
	push_up(p);
}

int query(int p,int pl,int pr) {
	if(!p) return 0;
	if(pl == pr) return pl;
	if(mx[ls[p]] >= mx[rs[p]]) return query(ls[p],pl,mid);
	else return query(rs[p],mid + 1,pr);
}

int merge(int x,int y,int pl,int pr) {
	if(!x || !y) return x + y;
	if(pl == pr) {
		mx[x] += mx[y];
		return x;
	}
	ls[x] = merge(ls[x],ls[y],pl,mid);
	rs[x] = merge(rs[x],rs[y],mid+1,pr);
	push_up(x);
	return x;
}

}


void dfs(int x,int f) {
	dep[x] = dep[f] + 1;
	for(int i = head[x];~i;i = nxt[i]) {
		int y = to[i];
		if(y ^ f) {
			dfs(y,x);
			rt[x] = sgt::merge(rt[x],rt[y],1,n);
		}
	}
	sgt::update(rt[x],1,n,dep[x]);
	a[x] = sgt::query(rt[x],1,n) - dep[x];
}

signed main() {
	init();
	n = rd();
	for(int i = 1;i<n;i++) {
		int u = rd(),v = rd();
		add(u,v);add(v,u);
	}
	dfs(1,0);
	for(int i = 1;i<=n;i++) wt(a[i]),putchar('\n');
	return 0;
}
  • 8
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值