HDU - 5029 Relief grain 树链剖分 + 线段树好题

题意:给出一棵n个节点的树,和m次操作。 操作a,b,k相当于将树上a,b结点间的路径上的节点都加上一个type k,最后输出每个结点被加最多次的那个type, 若有多个type被加的次数相同,输出编号最小的type。

思路:显然要先树链剖分将树上操作变成线性序列的操作,emmm,然后我就不会了。。

正解:树链剖分后考虑如何维护更新操作,对于一个操作a,b,k,我们可以在pos[a]位置打上一个k标记,在pos[b]位置打上一个-k标记,这里pos[]为树链剖分后的新编号,同一个点的多个标记用vector存储,这里要注意的是,由于我们要更新a->b路径上所有点,因此要给这条树链剖分出来的所有的重链的首尾两端打上标记(不完整的重链就打到结束处),至于为什么只打首尾点,我们后面说。

将所有标记都打完以后,我们就要开始用线段树维护每种type的被加次数了,从pos[1]开始更新线段树,先将pos[1]位置的所有正值对应的type在线段树中+1,然后询问最大值被加次数对应的type,这里因为每次都是询问最大被加次数对应的type,因此我们push up 以后直接输出线段树第一个节点的值就好了,然后再将pos[1]位置的所有负值对应的type在线段树中-1。  对pos[2]重复上面的操作,直到最后所有答案就都求出来了。


考虑上面的做法,如果我们能把整个a->b的树链做成一个连续的序列,那么我们只需在这个序列的首尾各加一个标记就能实现差分询问了,但是由于时间空间限制我们不可能每次将这个树链取出来,也不可能把所有树链都存下来,但是我们有树链剖分这种好东西呀,我们虽然不能把a->b的序列做成一个连续的序列,但是我们可以把它做成logn个连续的序列呀,然后在这logn个连续的序列头尾打标记就好了。

这种差分的思想真的是非常妙啊!

代码:

#include<bits/stdc++.h>
#define ll long long
#define rank Rank
#define MAXN 100010
#define inf 0x3f3f3f3f
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
#define MID int mid = (l + r) >> 1; 
using namespace std;
typedef pair<int,int>P;
int fa[MAXN], pos[MAXN], rank[MAXN], son[MAXN], sz[MAXN];
int top[MAXN], dep[MAXN], ans[MAXN];
int num[MAXN << 2], MAX[MAXN << 2];
int tid;
vector<int> mp[MAXN], Q[MAXN];
void init(int n)
{
	tid = 0;
	for(int i = 0; i <= n; i++) mp[i].clear(), Q[i].clear();
	memset(son, -1, sizeof(int) * (n + 5));
	memset(num, 0, sizeof(num));
	memset(MAX, 0, sizeof(MAX));
}
void dfs1(int u)
{
	sz[u] = 1;
	int v;
	for(int i = 0; i < mp[u].size(); i++)
	{
		v = mp[u][i];
		if(v == fa[u]) continue;
		fa[v] = u; dep[v] = dep[u] + 1;
		dfs1(v);
		sz[u] += sz[v];
		if(son[u] == -1 || sz[v] > sz[son[u]])
		son[u] = v;
	}
}
void dfs2(int u, int head)
{
	top[u] = head;
	pos[u] = ++tid;
	rank[tid] = u;
	if(son[u] == -1) return ;
	dfs2(son[u], head);
	int v;
	for(int i = 0; i < mp[u].size(); i++)
	{
		v = mp[u][i];
		if(v == son[u] || v == fa[u]) continue;
		dfs2(v, v);
	}
}
void push_up(int rt)
{
	num[rt] = max(num[rt << 1], num[rt << 1 | 1]);
	if(num[rt << 1] >= num[rt << 1 | 1]) MAX[rt] = MAX[rt << 1];
	else MAX[rt] = MAX[rt << 1 | 1];
}
void update(int id, int x, int l, int r, int rt)
{
	if(l == r)
	{
		num[rt] += x;
		MAX[rt] = id;
		return ;
	}
	MID
	if(id <= mid)
	update(id, x, lson);
	if(id > mid)
	update(id, x, rson);
	push_up(rt);
}
void work(int u, int v, int w)
{
	int f1 = top[u], f2 = top[v];
	while(f1 != f2)
	{
		if(dep[f1] < dep[f2])
		swap(f1, f2), swap(u, v);
		Q[pos[f1]].push_back(w);
		Q[pos[u]].push_back(-w);
		u = fa[f1], f1 = top[u];
	}
	if(dep[u] > dep[v]) swap(u, v);
	Q[pos[u]].push_back(w);
	Q[pos[v]].push_back(-w);
}
void solve(int n, int N)
{
	for(int i = 1; i <= n; i++)
	{
		for(int j = 0; j < Q[i].size(); j++)
		if(Q[i][j] > 0) update(Q[i][j], 1, 1, N, 1);
		ans[rank[i]] = MAX[1];
		for(int j = 0; j < Q[i].size(); j++)
		if(Q[i][j] < 0) update(-Q[i][j], -1, 1, N, 1);
	}
	for(int i = 1; i <= n; i++)
	printf("%d\n", ans[i]);
}
int main()
{
	int n, m, u, v, w;
	while(~scanf("%d %d", &n, &m), n + m)
	{
		init(n);
		for(int i = 1; i < n; i++)
		{
			scanf("%d %d", &u, &v);
			mp[u].push_back(v);
			mp[v].push_back(u);
		}
		fa[1] = dep[1] = 0;
		dfs1(1);
		dfs2(1, 1);
		int up = 0;
		while(m--)
		{
			scanf("%d %d %d", &u, &v, &w);
			work(u, v, w);
			up = max(w, up);
		}
		solve(n, up);
	}
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值