bzoj 3307: 雨天的尾巴 线段树合并

题目大意:

N个点,形成一个树状结构。有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品。问完成所有发放后,每个点存放最多的是哪种物品。

题解:

首先我们为每一个节点都开一个线段树
然后我们进行树上路径差分:
对于一个从u->v的路径,我们将其分解为:
u->1 , v->1,fa[lca(u,v)] -> 1
lca表示最近公共祖先,fa[x]为x的父亲节点
所以我们需要附加权值
我们可以在u的线段树上加上1
在v的线段树上加上1
在lca(u,v)的线段树上加上-1
在fa[lca(u,v)]的线段树上加上-1
然后我们自叶子向根依次进行线段树的合并即可

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
	x=0;char ch;bool flag = false;
	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
inline int cat_max(const int &a,const int &b){return a>b ? a:b;}
inline int cat_min(const int &a,const int &b){return a<b ? a:b;}
const int maxn = 110010;
const int inf = 0x3f3f3f3f;
struct Edge{
	int to,next;
}G[maxn<<1];
int head[maxn],cnt;
void add(int u,int v){
	G[++cnt].to = v;
	G[cnt].next = head[u];
	head[u] = cnt;
}
#define v G[i].to
int siz[maxn],son[maxn],fa[maxn],dep[maxn];
int top[maxn];
void dfs(int u){
	siz[u] = 1;
	for(int i = head[u];i;i=G[i].next){
		if(v == fa[u]) continue;
		dep[v] = dep[u] + 1;
		fa[v] = u;
		dfs(v);
		siz[u] += siz[v];
		if(siz[son[u]] < siz[v]) son[u] = v;
	}
}
void dfs(int u,int tp){
	top[u] = tp;
	if(son[u]) dfs(son[u],tp);
	for(int i = head[u];i;i=G[i].next){
		if(v == fa[u] || v == son[u]) continue;
		dfs(v,v);
	}
}
#undef v
inline int lca(int u,int v){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]]) swap(u,v);
		u = fa[top[u]];
	}return dep[u] < dep[v] ? u : v;
}
struct Node{
	Node *ch[2];
	int mx,id;
	void update();
}*null;
void Node::update(){
	mx = max(ch[0]->mx,ch[1]->mx);
	if(mx == ch[0]->mx) id = ch[0]->id;
	else if(mx == ch[1]->mx) id = ch[1]->id;
}
Node mem[maxn<<6],*it;
inline void init(){
	it = mem;null = it++;
	null->ch[0] = null->ch[1] = null;
	null->mx = -inf;null->id = -1;
}
inline Node* newNode(){
	Node *p = it++;p->ch[0] = p->ch[1] = null;
	p->mx = -inf;p->id = 0;return p;
}
void merge(Node* &x,Node *y){
	if(x == null){x = y;return;}
	if(x->ch[0] == null || y->ch[0] == null){
		x->ch[0] = y->ch[0] == null ? x->ch[0] : y->ch[0];
	}else merge(x->ch[0],y->ch[0]);
	if(x->ch[1] == null || y->ch[1] == null){
		x->ch[1] = y->ch[1] == null ? x->ch[1] : y->ch[1];
	}else merge(x->ch[1],y->ch[1]);
	if(x->ch[0] == x->ch[1] && x->ch[0] == null){
		x->mx += y->mx;
	}else x->update();
	return;
}
void insert(Node* &p,int l,int r,int pos,int d){
	if(p == null) p = newNode();
	if(l == r){
		p->id = pos;
		if(p->mx == -inf) p->mx = d;
		else p->mx += d;
		return;
	}
	int mid = (l+r) >> 1;
	if(pos <= mid) insert(p->ch[0],l,mid,pos,d);
	else insert(p->ch[1],mid+1,r,pos,d);
	p->update();return;
}
Node *root[maxn];
int anss[maxn];
#define v G[i].to
void dfss(int u,int fa){
	for(int i = head[u];i;i=G[i].next){
		if(v == fa) continue;
		dfss(v,u);
		merge(root[u],root[v]);
	}
	if(root[u]->id != -1) anss[u] = root[u]->id;
}
#undef v
int main(){
	init();
	int n,m;read(n);read(m);
	for(int i=0;i<=n;++i) root[i] = null;
	for(int i=1,u,v;i<n;++i){
		read(u);read(v);
		add(u,v);add(v,u);
	}dfs(1);dfs(1,1);
	for(int i=1,u,v,d;i<=m;++i){
		read(u);read(v);read(d);
		insert(root[u],1,1e9,d,1);
		insert(root[v],1,1e9,d,1);
		int lc = lca(u,v);
		insert(root[lc],1,1e9,d,-1);
		if(lc != 1) insert(root[fa[lc]],1,1e9,d,-1);
	}dfss(1,0);
	for(int i=1;i<=n;++i){
		printf("%d\n",anss[i]);
	}
	getchar();getchar();
	return 0;
}
posted @ 2017-02-26 21:29  Sky_miner  阅读(625)  评论(0编辑  收藏  举报