Codeforces Round #767 (Div. 1) E. Groceries in Meteor Town

题解还是妙啊。
考虑每条边在什么时候成为答案,可以把边按照权值排序,从大到小枚举,对于当前枚举的边,它能将树分成两部分,只要路径的起点和终点分别在这两部分,当前边就成为答案。然后可以删掉这条边,递归子部分操作。

如何快速求两个点路径之间的边权最大值呢?这里真的很妙,将每条边看作一个节点,按照上面的方法,每次删掉一条边,就将这条边所代表的节点和他分开的这两部分下一步要删的边分别连边,递归操作就可以形成一颗二叉树,而这个二叉树具有第一个极好的性质:我们将原树中每个点挂在他所连边代表的节点中深度最大的节点上,此时原树任意两点路径之间边权最大值就是他们在这棵二叉树上对应点的LCA代表的边的边权。

这棵二叉树如何建?按上面的操作并不好做,我们可以反着来,从叶子节点开始往上建树,将边从小到大排序,利用并查集来合并边两边部分的两棵子树,合并过程中新建节点代表当前边。这里是点的并查集,但同时是新建边代表的节点,别被绕晕了,需要好几个数组去弄才行。

我们已经能够在nlogn的良好的复杂度下求任意两点间路径上边权的最大值了,但是open stores有一群,如何求一个点和一群点的路径上边权的最大值呢?这里就要用到这棵二叉树第二个极好的性质了:在这棵树上父亲节点永远比儿子节点更优,我们实质上是要求当前点和一群点LCA中最优的,也就是求LCA最靠近根节点的即可,将这群点按照dfs序排序,那么事实上只有dfs序最小和dfs序最大的节点才有可能成为答案,我们只需要维护这个open stores集合中dfs序最小和最大的节点是什么即可,由于关闭和开放商店都是区间操作,可以直接用线段树打tag进行维护。

注意如果集合里只有一个点且当前点和询问点是同一个点,答案也是-1,由于可能有多个点挂在二叉树的同一个点上,所以这种情况需要特别考虑进行处理。

很妙的题。
代码如下:

#include <bits/stdc++.h>

using namespace std;

const int Maxn = 1e6 + 10;

struct node {
	int x, y, z;
}a[Maxn];

bool cmp (node u, node v) {
	return u.z < v.z;
}

int fa[Maxn];
int Root[Maxn];
int w[Maxn];
int Id[Maxn];

int find(int x) {
	return fa[x] = fa[x] == x ? x : find(fa[x]);
}

int Begin[Maxn], to[Maxn], e, Next[Maxn];

void Add(int x, int y) {
	to[++e] = y;
	Next[e] = Begin[x];
	Begin[x] = e;
}

void Addedge(int x, int y) {
	Add(x, y);
	Add(y, x);
}

int dfs_clock, dfn[Maxn], st_clock, st[Maxn], deep[Maxn], First[Maxn], dfs_pos[Maxn];

void Dfs(int h, int father) {
	dfn[++ dfs_clock] = h;
	dfs_pos[h] = dfs_clock;
	
	st[++ st_clock] = h;
	First[h] = st_clock;
	
	for (int i = Begin[h]; i; i = Next[i]) {
		int v = to[i];
		if (v == father) continue;
		deep[v] = deep[h] + 1;
		Dfs(v, h);
		st[++ st_clock] = h;
	}
}

int dp[Maxn][22];

void rmq() {
	for (int i = 1; i <= st_clock; ++i) {
		dp[i][0] = st[i];
	}
	
	for (int j = 1; j <= 20; ++j) {
		for (int i = 1; i <= st_clock; ++i) {
			if (i + (1 << j) > st_clock) break;
			if (deep[dp[i][j-1]] < deep[dp[i + (1 << j-1)][j-1]]) {
				dp[i][j] = dp[i][j-1];
			}
			else dp[i][j] = dp[i + (1 << j-1)][j-1];
		}
	}
}

int Query(int x, int y) {
	x = First[x];
	y = First[y];
	if (x > y) swap(x, y);
	
	int k = (int)log2(y - x + 1);
	if (deep[dp[x][k]] < deep[dp[y - (1 << k) + 1][k]]) return dp[x][k];
	return dp[y - (1 << k) + 1][k];
}

int sLeft[Maxn << 1], sRight[Maxn << 1];
int Left[Maxn << 1], Tag[Maxn << 1], Right[Maxn << 1]; 

void Build(int h, int l, int r) {
	if (l == r) {
		sLeft[h] = sRight[h] = dfs_pos[Id[l]];
		return;
	}
	
	int mid = l+r >> 1;
	Build(h<<1, l, mid);
	Build(h<<1|1, mid+1 ,r);
	
	sLeft[h] = min(sLeft[h<<1], sLeft[h<<1|1]);
	sRight[h] = max(sRight[h<<1], sRight[h<<1|1]);
}

void pushdown(int h, int l, int r) {
	if (Tag[h] == 0) return;
	if (Tag[h] == 1) {
		if (l == r) {
			Tag[h] = 0;
			Left[h] = sLeft[h];
			Right[h] = sRight[h];
			return;
		}
		
		Left[h] = sLeft[h];
		Right[h] = sRight[h];
		Tag[h<<1] = Tag[h];
		Tag[h<<1|1] = Tag[h];
		Tag[h] = 0;
		return;
	}
	
	else {
		if (l == r) {
			Tag[h] = 0;
			Left[h] = 0;
			Right[h] = 0;
			return;
		}
		
		Left[h] = 0;
		Right[h] = 0;
		Tag[h<<1] = Tag[h];
		Tag[h<<1|1] = Tag[h];
		Tag[h] = 0;
		return;
	}
}

void pushup(int h, int l, int r) {
	int mid = l+r >> 1;
	if (Tag[h<<1]) pushdown(h<<1, l, mid);
	if (Tag[h<<1|1]) pushdown(h<<1|1, mid+1, r);
	if (Left[h<<1] == 0) {
		Left[h] = Left[h<<1|1];
		Right[h] = Right[h<<1|1];
	}
	else if (Left[h<<1|1] == 0) {
		Left[h] = Left[h<<1];
		Right[h] = Right[h<<1];
	}
	else {
		Left[h] = min(Left[h<<1], Left[h<<1|1]);
		Right[h] = max(Right[h<<1], Right[h<<1|1]);
	}
}

void Insert(int h, int l, int r, int s, int e, int Type) {
	pushdown(h, l, r);
	if (l == s && r == e) {
		Tag[h] = Type;
		pushdown(h, l, r);
		return;
	}
	
	int mid = l+r >> 1;
	if (e <= mid) Insert(h<<1, l, mid, s, e, Type);
	else if (s > mid) Insert(h<<1|1, mid+1, r, s, e, Type);
	else {
		Insert(h<<1, l, mid, s, mid, Type);
		Insert(h<<1|1, mid+1, r, mid+1, e, Type);
	}
	
	pushup(h, l, r);
}

int Query_pos(int h, int l, int r, int pos) {
	pushdown(h, l, r);
	if (l == r) return Left[h];
	
	int mid = l+r >> 1;
	int Ans = 0;
	if (pos <= mid) Ans = Query_pos(h<<1, l, mid, pos);
	else Ans = Query_pos(h<<1|1, mid+1, r, pos);
	
	pushup(h, l, r);
	return Ans;
}

int main() {
	
	int n, q;
	scanf ("%d%d", &n, &q);
	for (int i = 1; i < n; ++i) {
		scanf ("%d%d%d", &a[i].x, &a[i].y, &a[i].z);
	}
	
	sort (a + 1, a + n, cmp);
	for (int i = 1; i <= n; ++i) fa[i] = i;
	
	int Cnt = 0;
	for (int i = 1; i < n; ++i) {
		int x = a[i].x, y = a[i].y;
		++ Cnt;
		if (!Id[x]) Id[x] = Cnt;
		if (!Id[y]) Id[y] = Cnt;
		if (Root[find(x)] != 0) Addedge(Root[find(x)], Cnt);
		if (Root[find(y)] != 0) Addedge(Root[find(y)], Cnt);
		fa[find(x)] = find(y);
		Root[find(y)] = Cnt;
		w[Cnt] = a[i].z;
	}
	
	Dfs(Root[find(1)], 0);
	rmq();
	
	Build(1, 1, n);
	
	for (int i = 1; i <= q; ++i) {
		int Type, l, r, x;
		scanf ("%d", &Type);
		if (Type == 1 || Type == 2) {
			scanf ("%d%d", &l, &r);
			Insert (1, 1, n, l, r, Type);
		}
		else {
			int CurLeft = Left[1];
			int CurRight = Right[1];
			
			scanf ("%d", &x);
			if (Left[1] == 0) printf ("-1\n");
			else {
				if (Query_pos(1, 1, n, x) != 0) {
					Insert(1, 1, n, x, x, 2);
					CurLeft = Left[1];
					CurRight = Right[1];
					Insert(1, 1, n, x, x, 1);
				}
				
				if (CurLeft == 0) printf ("-1\n");
				else printf ("%d\n", max(w[Query(dfn[CurLeft], Id[x])], w[Query(dfn[CurRight], Id[x])]) );
			}
		}
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值