[hihoCoder#1381]Little Y's Tree

[hihoCoder#1381]Little Y‘s Tree

试题描述

小Y有一棵n个节点的树,每条边都有正的边权。

小J有q个询问,每次小J会删掉这个树中的k条边,这棵树被分成k+1个连通块。小J想知道每个连通块中最远点对距离的和。

这里的询问是互相独立的,即每次都是在小Y的原树上进行操作。

输入

第一行一个整数n,接下来n-1行每行三个整数u,v,w,其中第i行表示第i条边边权为wi,连接了ui,vi两点。

接下来一行一个整数q,表示有q组询问。

对于每组询问,第一行一个正整数k,接下来一行k个不同的1到n-1之间的整数,表示删除的边的编号。

1<=n,q,Σk<=105, 1<=w<=109

输出

共q行,每行一个整数表示询问的答案。

输入示例

5
1 2 2
2 3 3
2 4 4
4 5 2
3
4 1 2 3 4
1 4
2 2 3

输出示例

0
7
4

数据规模及约定

见“输入

题解

我们将每次删除的所有边的深度较大的节点作为关键点,建立虚树。然后我们发现我们可以维护一下区间的连通性,将所有节点按照 dfs 序从小到大排序以后,用线段树合并连通信息。对于两个连通块 A 和 B,若 A 的直径为 (a, b),B 的直径为 (c, d),那么 A U B 的直径就是 (a, b), (c, d), (a, c), (a, d), (b, c), (b, d) 六种情况,我们去一个最大值即可。对于一颗虚树,我们按照深度从大到小依次查询该节点对应区间的连通块直径,累计答案,再将这个区间打上删除标记,最后记得要恢复删除标记。

对了,这题要 O(1) 求 LCA。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <algorithm>
using namespace std;

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
	return x * f;
}

#define maxn 100010
#define maxm 200010
#define maxlog 20
#define oo 2147483647
#define LL long long
int n, m, head[maxn], Next[maxm], To[maxm], dist[maxm];
struct Edge {
	int a, b, c;
	Edge() {}
	Edge(int _1, int _2, int _3): a(_1), b(_2), c(_3) {}
} es[maxn];

void AddEdge(int a, int b, int c) {
	To[++m] = b; dist[m] = c; Next[m] = head[a]; head[a] = m;
	swap(a, b);
	To[++m] = b; dist[m] = c; Next[m] = head[a]; head[a] = m;
	return ;
}

int dep[maxn], list[maxm], cl, pos[maxn], ord[maxn], clo, ordr[maxn], Pos[maxn];
LL Dep[maxn];
void build(int u, int fa) {
	list[++cl] = u; pos[u] = cl; ord[u] = ++clo; Pos[clo] = u;
	for(int e = head[u]; e; e = Next[e]) if(To[e] != fa) {
		dep[To[e]] = dep[u] + 1;
		Dep[To[e]] = Dep[u] + dist[e];
		build(To[e], u);
		list[++cl] = u;
	}
	ordr[u] = clo;
	return ;
}
int Lca[maxlog][maxm], Log[maxm];
void rmq_init() {
	Log[1] = 0;
	for(int i = 2; i <= cl; i++) Log[i] = Log[i>>1] + 1;
	for(int i = 1; i <= cl; i++) Lca[0][i] = list[i];
	for(int j = 1; (1 << j) <= cl; j++)
		for(int i = 1; i + (1 << j) - 1 <= cl; i++) {
			int a = Lca[j-1][i], b = Lca[j-1][i+(1<<j-1)];
			if(dep[a] < dep[b]) Lca[j][i] = a;
			else Lca[j][i] = b;
		}
	return ;
}
int lca(int a, int b) {
	int l = min(pos[a], pos[b]), r = max(pos[a], pos[b]);
	int t = Log[r-l+1];
	a = Lca[t][l]; b = Lca[t][r-(1<<t)+1];
	return dep[a] < dep[b] ? a : b;
}
LL calc(int a, int b) {
	return Dep[a] + Dep[b] - (Dep[lca(a,b)] << 1);
}

struct Node {
	bool hasv;
	int A, B; LL Len;
	Node() {}
	Node(int _1, int _2, LL _3, bool _4): A(_1), B(_2), Len(_3), hasv(_4) {}
} ns[maxn<<2];
void maintain(Node& o, Node lc, Node rc) {
	o.Len = -1;
	if(!lc.hasv && !rc.hasv){ o.Len = o.hasv = 0; return ; }
	if(!lc.hasv) {
		o = rc;
		return ;
	}
	if(!rc.hasv) {
		o = lc;
		return ;
	}
	o.hasv = 1;
	if(o.Len < lc.Len) o.Len = lc.Len, o.A = lc.A, o.B = lc.B;
	if(o.Len < rc.Len) o.Len = rc.Len, o.A = rc.A, o.B = rc.B;
	LL d = calc(lc.A, rc.A);
	if(o.Len < d) o.Len = d, o.A = lc.A, o.B = rc.A;
	d = calc(lc.A, rc.B);
	if(o.Len < d) o.Len = d, o.A = lc.A, o.B = rc.B;
	d = calc(lc.B, rc.A);
	if(o.Len < d) o.Len = d, o.A = lc.B, o.B = rc.A;
	d = calc(lc.B, rc.B);
	if(o.Len < d) o.Len = d, o.A = lc.B, o.B = rc.B;
	return ;
}
void build(int L, int R, int o) {
	if(L == R) ns[o] = Node(Pos[L], Pos[R], 0, 1);
	else {
		int M = L + R >> 1, lc = o << 1, rc = lc | 1;
		build(L, M, lc); build(M+1, R, rc);
		maintain(ns[o], ns[lc], ns[rc]);
		ns[o].hasv = 1;
	}
//	printf("seg[%d, %d]: %d %d %lld\n", L, R, ns[o].A, ns[o].B, ns[o].Len);
	return ;
}
void update(int L, int R, int o, int ql, int qr, bool v) {
	if(ql <= L && R <= qr) ns[o].hasv = v;
	else {
		int M = L + R >> 1, lc = o << 1, rc = lc | 1;
		if(ql <= M) update(L, M, lc, ql, qr, v);
		if(qr > M) update(M+1, R, rc, ql, qr, v);
		maintain(ns[o], ns[lc], ns[rc]);
	}
	return ;
}
Node query(int L, int R, int o, int ql, int qr) {
	if(ql <= L && R <= qr) return ns[o];
	int M = L + R >> 1, lc = o << 1, rc = lc | 1;
	Node ans(-1, -1, -1, 0);
	if(ql <= M) {
		Node tmp = query(L, M, lc, ql, qr), tt(0, 0, -1, 1);
		maintain(tt, tmp, ans);
		if(tmp.hasv) ans = tt;
	}
	if(qr > M) {
		Node tmp = query(M+1, R, rc, ql, qr), tt(0, 0, -1, 1);
		maintain(tt, tmp, ans);
		if(tmp.hasv) ans = tt;
	}
//	printf("[%d, %d] %d %d %d %lld(%d)\nans: %lld\n", L, R, o, ql, qr, ns[o].Len, ns[o].hasv, ans.Len);
	return ans;
}

bool cmp(int a, int b) { return pos[a] < pos[b]; }
int psi[maxn], cpi, ps[maxn], cp, vis[maxn];
bool flg[maxn];

struct Vtree {
	int m, head[maxn], Next[maxm], To[maxm];
	LL dist[maxm], ans;
	void init() {
		ans = m = 0;
		return ;
	}
	void AddEdge(int a, int b, LL c) {
//		printf("Add2: %d %d %lld\n", a, b, c);
		To[++m] = b; dist[m] = c; Next[m] = head[a]; head[a] = m;
		swap(a, b);
		To[++m] = b; dist[m] = c; Next[m] = head[a]; head[a] = m;
		return ;
	}
	void dfs(int u, int fa) {
		for(int e = head[u]; e; e = Next[e]) if(To[e] != fa)
			dfs(To[e], u);
		if(flg[u]) {
			Node tmp = query(1, n, 1, ord[u], ordr[u]); flg[u] = 0;
			if(tmp.hasv) ans += tmp.Len;
//			printf("at %d tmp: %lld [%d, %d]\n", u, tmp.Len, ord[u], ordr[u]);
			update(1, n, 1, ord[u], ordr[u], 0);
		}
		return ;
	}
	void dfs2(int u, int fa) {
		for(int e = head[u]; e; e = Next[e]) if(To[e] != fa)
			dfs2(To[e], u);
		update(1, n, 1, ord[u], ordr[u], 1);
		head[u] = 0;
		return ;
	}
} vt;

int main() {
	n = read();
	for(int i = 1; i < n; i++) {
		int a = read(), b = read(), c = read();
		AddEdge(a, b, c);
		es[i] = Edge(a, b, c);
	}
	build(1, 0);
	rmq_init();
//	for(int i = 1; i <= cl; i++) printf("%d%c", Lca[0][i], i < cl ? ‘ ‘ : ‘\n‘);
//	for(int i = 1; i <= n; i++) printf("%d%c", pos[i], i < n ? ‘ ‘ : ‘\n‘);
	build(1, n, 1);
	int q = read();
//	for(int i = 1; i <= n; i++) printf("%d%c", ord[i], i < n ? ‘ ‘ : ‘\n‘);
	while(q--) {
		int cpi = read(); cp = 0;
		for(int i = 1; i <= cpi; i++) {
			int e = read(), u = dep[es[e].a] < dep[es[e].b] ? es[e].b : es[e].a;
			ps[++cp] = psi[i] = u;
			vis[ps[cp]] = q + 1;
			flg[ps[cp]] = 1;
		}
		if(vis[1] != q + 1) ps[++cp] = 1, flg[1] = 1;
		sort(psi + 1, psi + cpi + 1, cmp);
		for(int i = 1; i < cpi; i++) {
			int c = lca(psi[i], psi[i+1]);
			if(vis[c] != q + 1) vis[c] = q + 1, ps[++cp] = c;
		}
		sort(ps + 1, ps + cp + 1, cmp);
//		printf("mother_fuck: "); for(int i = 1; i <= cp; i++) printf("%d%c", ps[i], i < cp ? ‘ ‘ : ‘\n‘);
		vt.init();
		for(int i = 1; i < cp; i++) {
			int a = ps[i], b = ps[i+1], c = lca(a, b);
			vt.AddEdge(b, c, Dep[b] - Dep[c]);
		}
		vt.dfs(1, 0);
		vt.dfs2(1, 0);
		printf("%lld\n", vt.ans);
	}

	return 0;
}
/*
13
1 2 1
1 3 1
2 4 1
2 5 1
2 6 1
3 7 1
3 8 1
3 9 1
7 12 1
12 13 1
9 10 1
10 11 1
*/

[hihoCoder#1381]Little Y's Tree

时间: 2024-10-19 10:03:25

[hihoCoder#1381]Little Y's Tree的相关文章

spoj 375 Query on a tree (树链剖分)

Query on a tree You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. We will ask you to perfrom some instructions of the following form: CHANGE i ti : change the cost of the i-th edge to ti or Q

linux命令(5)---tree命令

tree命令:tree - list contents of directories in a tree-like format [功能] tree命令用于显示目录层级结构关系,常用就是单独tree命令,选项很多可根据日后工作中使用到的在进行查询man帮助 这个命令一般linux系统不自带需要手动安装,安装方法:可挂载光盘切换至光盘目录下,查找安装包名再使用rpm -ivh 安装 语法: tree [option] DIR 安装实例 [[email protected] ~]# mkdir /m

菜鸟日志(一)之CentOS6.5下的tree命令

在学习python开发的基础课程时,老师有讲到最基础的50个linux的命令,其中“tree”命令可以显示一个目录的树结构(如在此目录下还有数个子目录的话) 但是在初次使用时,系统提示tree:command not found,这是因为在初次安装的CentOS中,“tree”命令没有安装,解决方法: [[email protected] ~]# yum -y install tree 安装完成后,即可使用“tree”命令! 使用效果: [[email protected] terminfo]#

HDU3333 Turing Tree(线段树)

题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=3333 Description After inventing Turing Tree, 3xian always felt boring when solving problems about intervals, because Turing Tree could easily have the solution. As well, wily 3xian made lots of new

Splay Tree(伸展树)

参考:<数据结构(C++语言版)>邓俊辉著 (好书 一. 伸展树(由 D. D. Sleator 和 R. E. Tarjan 于 1985 年发明)也是平衡二叉搜索树的一种形式.相对于 AVL 树,伸展树的实现更为简洁 伸展树无需时刻都严格地保持全树的平衡,但却能够在任何足够长的真实操作序列中,保持分摊意义上的高效率 伸展树也不需要对基本的二叉树节点结构做任何附加的要求或改动,更不需要记录平衡因子或高度之类的额外信息,故适用范围更广 二.局部性 信息处理的典型模式是,将所有的数据项视作一个集

POJ3321 Apple Tree(DFS序)

题目,是对一颗树,单点修改.子树查询.典型的dfs序入门题. DFS序可以将一颗树与子树们表示为一个连续的区间,然后用线段树来维护:感觉算是树链剖分的一种吧,和轻重链剖分不同的是这是对子树进行剖分的. 我用非递归方式求DFS序. 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 #define MAXN 111111 6 struct Edge{ 7

ZOJ 3686 A Simple Tree Problem(线段树)

A Simple Tree Problem Time Limit: 3 Seconds      Memory Limit: 65536 KB Given a rooted tree, each node has a boolean (0 or 1) labeled on it. Initially, all the labels are 0. We define this kind of operation: given a subtree, negate all its labels. An

hust 1016 Black-White Tree

题目描述 在图论中,包含n个结点(结点编号为1~n).n-1条边的无向连通图被称为树. 在树中,任意一对结点间的简单路径总是惟一的. 你拥有一棵白色的树——所有节点都是白色的.接下来,你需要处理c条指令: 修改指令(0 v):改变一个给定结点的颜色(白变黑,黑变白): 查询指令(1 v):询问从结点1到一个给定结点所经过的第一个黑色结点编号(假设沿着简单路径走). 注意,在查询指令中,必须从结点1而不是结点v出发.如果结点1本身就是黑色,查询指令应该返回1. 输入 第一行包含两个正整数n, c,

Codeforces 620E New Year Tree(DFS序+线段树)

题目大概说给一棵树,树上结点都有颜色(1到60),进行下面两个操作:把某结点为根的子树染成某一颜色.询问某结点为根的子树有多少种颜色. 子树,显然DFS序,把子树结点映射到连续的区间.而注意到颜色60种,这样就可以用一个64位整型去表示颜色的集合,然后就是在这个连续区间中用线段树成段更新颜色集合和区间查询颜色集合了. 1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 #define MAXN 500000