HDU 5296 Annoying problem(点到树链的距离+LCA)



题目大意:给一棵带权树,和一个集合S,初始为空。现有两种操作:

1、选择某个顶点加入S

2、将S中某个顶点去掉。

问:每次操作后,使得集合S中的点联通的最小边权和是多少。

思路:首先这道题可以用到一个公式,就是树上的点到某一个树链的距离,假设这个点是n,一条树链的两个端点是u, v,那么点到这条树链的距离

D = dist(n) - dist( lca(n, u) ) - dist( lca(n, v) ) + dist( lca(u, v) ),这一步可以画画图感受下。

有了这个铺垫就可以用它来解题了,首先预处理出每个结点的dfs序,以增加节点i为例,假设存在两个节点,且两个节点的dfs序恰好一个比i大一个比i小,那么增加的距离就是i到这条链的距离。

如果所有节点的dfs序都比i大或都比i小,那么取dfs序最大和最小的两个节点,那么增加的距离就是i到这条链的距离。

对于删除操作其实和增加操作等价,可以先删除这个节点然后按照增加节点来做即可。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

const int MAXN = 100100;
//const int INF = 0x3f3f3f3f;
int n, q, clk, tim[MAXN];
int dist[MAXN], pnt[MAXN], ans[4*MAXN], arc[MAXN];
bool vis[MAXN];
vector<int> G[MAXN], W[MAXN], query[MAXN], num[MAXN];
int Find(int x) {
	if(x == pnt[x]) return x;
	return pnt[x] = Find(pnt[x]);
}
void Tarjan(int u) {
	vis[u] = 1; pnt[u] = u;
	int sz1 = G[u].size();
	for(int i = 0; i < sz1; i++) {
		int v = G[u][i];
		if(vis[v]) continue;
		Tarjan(v);
		pnt[v] = u;
	}
	int sz2 = query[u].size();
	for(int i = 0; i < sz2; i++) {
		int v = query[u][i];
		if(vis[v]) ans[num[u][i]] = dist[Find(v)];
		//cout << u << " " << i << " " << sz2 << endl;
	}
} 
void dfs(int cur, int fa, int val) {
	tim[cur] = ++clk; 
	arc[clk] = cur;
	dist[cur] = val;
	for(int i = 0; i < G[cur].size(); i++) {
		int u = G[cur][i];
		if(u == fa) continue;
		dfs(u, cur, val+W[cur][i]);
	}
}
set<int> s;
void init() {
	clk = 0;
	s.clear();
	memset(vis, 0, sizeof(vis));
	for(int i = 1; i <= n; i++) {
		G[i].clear(); 
		W[i].clear(); 
		query[i].clear(); 
		num[i].clear();
	}
}
int upd[MAXN];
int main() {
    //freopen("input.txt", "r", stdin);
	int T; cin >> T;
	int kase = 0;
	while(T--) {
		cin >> n >> q;
		init();
		for(int i = 1, u, v, d; i < n; i++) {
			scanf("%d%d%d", &u, &v, &d);
			G[u].push_back(v);
			G[v].push_back(u);
			W[u].push_back(d);
			W[v].push_back(d);
		}
		dfs(1, 0, 0);
		for(int i = 1, u, v; i <= q; i++) {
			scanf("%d%d", &u, &v);
			if(u == 1) {
				if(!s.count(tim[v])) {
					if(s.empty()) {
						s.insert(tim[v]);
						upd[i] = 0;
					}
					else if(s.size() == 1) {
						int t = *s.begin();
						ans[4*i] = dist[arc[t]];
						query[arc[t]].push_back(v);
						num[arc[t]].push_back(4*i+1);
						query[v].push_back(arc[t]);
						num[v].push_back(4*i+1);
						query[arc[t]].push_back(v);
						num[arc[t]].push_back(4*i+2);
						query[v].push_back(arc[t]);
						num[v].push_back(4*i+2);
						ans[4*i+3] = dist[v];
						s.insert(tim[v]);
						upd[i] = 1;
					}
					else {
						int t1, t2;
						if(tim[v]<*s.begin() || tim[v]>*s.rbegin()) t1 = *s.begin(), t2 = *s.rbegin();
						else {
							set<int>::iterator it1, it2;
							it1 = s.upper_bound(tim[v]);
							t2 = *it1;
							it2 = --it1;
							t1 = *it2;
						}
						query[arc[t1]].push_back(arc[t2]);
						query[arc[t2]].push_back(arc[t1]);
						num[arc[t1]].push_back(4*i);
						num[arc[t2]].push_back(4*i);
						query[v].push_back(arc[t1]);
						query[arc[t1]].push_back(v);
						num[v].push_back(4*i+1);
						num[arc[t1]].push_back(4*i+1);
						query[v].push_back(arc[t2]);
						query[arc[t2]].push_back(v);
						num[v].push_back(4*i+2);
						num[arc[t2]].push_back(4*i+2);
						ans[4*i+3] = dist[v];
						s.insert(tim[v]);
						upd[i] = 1;
					}
				}
				else upd[i] = 0;
			}
			else {
				if(s.count(tim[v])) {
					s.erase(tim[v]);
					if(s.empty()) {
						upd[i] = 0;
					}
					else if(s.size() == 1) {
						int t = *s.begin();
						ans[4*i] = dist[arc[t]];
						query[arc[t]].push_back(v);
						num[arc[t]].push_back(4*i+1);
						query[v].push_back(arc[t]);
						num[v].push_back(4*i+1);
						query[arc[t]].push_back(v);
						num[arc[t]].push_back(4*i+2);
						query[v].push_back(arc[t]);
						num[v].push_back(4*i+2);
						ans[4*i+3] = dist[v];
						upd[i] = 2;
					}
					else {
						int t1, t2;
						if(tim[v]<*s.begin() || tim[v]>*s.rbegin()) t1 = *s.begin(), t2 = *s.rbegin();
						else {
							set<int>::iterator it1, it2;
							it1 = s.upper_bound(tim[v]);
							t2 = *it1;
							it2 = --it1;
							t1 = *it2;
						}
						query[arc[t1]].push_back(arc[t2]);
						query[arc[t2]].push_back(arc[t1]);
						num[arc[t1]].push_back(4*i);
						num[arc[t2]].push_back(4*i);
						query[v].push_back(arc[t1]);
						query[arc[t1]].push_back(v);
						num[v].push_back(4*i+1);
						num[arc[t1]].push_back(4*i+1);
						query[v].push_back(arc[t2]);
						query[arc[t2]].push_back(v);
						num[v].push_back(4*i+2);
						num[arc[t2]].push_back(4*i+2);
						ans[4*i+3] = dist[v];
						upd[i] = 2;
					}
				}
				else upd[i] = 0;
			} 
		}
		Tarjan(1);
		printf("Case #%d:\n", ++kase);
		int ret = 0;
		for(int i = 1; i <= q; i++) {
			if(!upd[i]) printf("%d\n", ret);
			else if(upd[i] == 1) {
				ret = ret + ans[i*4] - ans[i*4+1] - ans[i*4+2] + ans[i*4+3];
				printf("%d\n", ret);
			}
			else {
				ret = ret - ans[i*4] + ans[i*4+1] + ans[i*4+2] - ans[i*4+3];
				printf("%d\n", ret);
			}
		}
	}
    return 0;
}

















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值