【BZOJ3307】雨天的尾巴(樹鏈剖分+樹上差分+線段樹)

3307: 雨天的尾巴

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 654  Solved: 270
[Submit][Status][Discuss]

Description

N個點,形成一個樹狀結構。有M次發放,每次選擇兩個點x,y
對於x到y的路徑上(含x,y)每個點發一袋Z類型的物品。完成
所有發放後,每個點存放最多的是哪種物品。

Input

第一行數字N,M
接下來N-1行,每行兩個數字a,b,表示a與b間有一條邊
再接下來M行,每行三個數字x,y,z.如題

Output


輸出有N行
每i行的數字表示第i個點存放最多的物品是哪一種,如果有
多種物品的數量一樣,輸出編號最小的。如果某個點沒有物品
則輸出0

Sample Input

20 50
8 6
10 6
18 6
20 10
7 20
2 18
19 8
1 6
14 20
16 10
13 19
3 14
17 18
11 19
4 11
15 14
5 18
9 10
12 15
11 14 87
12 1 87
14 3 84
17 2 36
6 5 93
17 6 87
10 14 93
5 16 78
6 15 93
15 5 16
11 8 50
17 19 50
5 4 87
15 20 78
1 17 50
20 13 87
7 15 22
16 11 94
19 8 87
18 3 93
13 13 87
2 1 87
2 6 22
5 20 84
10 12 93
18 12 87
16 10 93
8 17 93
14 7 36
7 4 22
5 9 87
13 10 16
20 11 50
9 16 84
10 17 16
19 6 87
12 2 36
20 9 94
9 2 84
14 1 94
5 5 94
8 17 16
12 8 36
20 17 78
12 18 50
16 8 94
2 19 36
10 18 36
14 19 50
4 12 50

Sample Output

87
36
84
22
87
87
22
50
84
87
50
36
87
93
36
94
16
87
50
50



1<=N,M<=100000
1<=a,b,x,y<=N
1<=z<=10^9

HINT

Source

[Submit][Status][Discuss]

-------------------------------------------------------------------------------

算法:
樹鏈剖分 + 樹上差分 + 線段樹

做法:

如果只在一個數列上進行 [ l , r ] 區間加,那麼我們只需在 l 處 +1,在 r+1 處 -1,最後統計答案時從頭到尾掃一遍即可。如果在一棵樹上的一條路徑上進行區間加,那麼我們就要在它的 dfs 序上進行差分。

這道題還是在樹上加上不同的物品,那麼我們在發放物品時,先像差分一樣那麼發放,記錄下發放的種類。在統計答案時,讓 dfs 序從 1 到 n 枚舉各個點,維護一個物品種類的線段樹,讓整棵線段樹只表示這一個點,由於差分的連續的性質,我們在處理下一個節點是隻需在原線段樹上進行一些物品的加加減減即可,每次操作都是 O(log tot ),tot 表示物品種類數。

程序細節:

具體實現中也有一些細節,就我的程序來說,我用建了一個圖來記錄每次在哪一個節點發了 ±1 個哪種物品,這只是用圖來記錄數據,而不是真正的圖論。細節都標在程序裏了。

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

inline int read(){
	int x=0, res=1; char c=getchar();
	while(!(c>='0'&&c<='9')){ if(c=='-') res=-1; c=getchar(); }
	while(c>='0'&&c<='9') x=x*10+c-'0', c=getchar();
	return x*res;
}

const int N=100010;
int n, m, tot, di, dti;
int head[N], wch[N], ans[N];
int dep[N], size[N], fa[N], son[N], dfn[N], Rank[N], top[N];
map <int,int> kind;

struct Edge{int to, next; }e[N<<1];
struct Data{int z, next, val; }dt[N*30];
struct Node{int typ, num; }nd[N<<2];

inline void add_edge(int u,int v){
	e[++tot] = (Edge){v,head[u]}; head[u]=tot;
	e[++tot] = (Edge){u,head[v]}; head[v]=tot;
}

inline void add_data(int u,int v,int val){
	dt[++dti] = (Data){v,head[u],val}; head[u]=dti; 
}

void dfs1(int u){
	size[u] = 1;
	for(int p=head[u], v; p; p=e[p].next){
		v = e[p].to;
		if(v==fa[u]) continue;
		dep[v] = dep[u]+1; fa[v] = u;
		dfs1(v);
		size[u] += size[v];
		if(son[u]==0 || size[son[u]]<size[v]) son[u]=v;
	}
}

void dfs2(int u,int tp){
	top[u] = tp; dfn[u] = ++di; Rank[di] = u;
	if(son[u]) dfs2(son[u], tp);
	for(int p=head[u]; p; p=e[p].next) if(e[p].to!=fa[u] && e[p].to!=son[u]) dfs2(e[p].to,e[p].to);
}

inline bool cmp(Node &x,Node &y){
	return (x.num<y.num) || (x.num==y.num && wch[x.typ]>wch[y.typ]);
}

inline void merge(int rt){
	nd[rt].num = -1;
	if(cmp(nd[rt],nd[rt<<1])){ nd[rt].num=nd[rt<<1].num; nd[rt].typ=nd[rt<<1].typ; }
	if(cmp(nd[rt],nd[rt<<1|1])){ nd[rt].num=nd[rt<<1|1].num; nd[rt].typ=nd[rt<<1|1].typ; }
}

void build(int l,int r,int rt){
	if(l==r) nd[rt].typ=r, nd[rt].num=0;
	else{
		int mid = l+r>>1;
		build(l,mid,rt<<1);
		build(mid+1,r,rt<<1|1);
		merge(rt);
	}
}

void change(int l,int r,int rt,int x,int val){
	if(l==r) nd[rt].num+=val;
	else{
		int mid = l+r>>1;
		if(x<=mid) change(l,mid,rt<<1,x,val);
		else change(mid+1,r,rt<<1|1,x,val);
		merge(rt);
	}
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1; i<n; ++i){ add_edge(read(), read()); }
	dep[1] = 1; dfs1(1); dfs2(1,1);
	memset(head,0,sizeof(head)); tot=0;	// 當這棵樹有了靈魂(dep,fa,top,dfn,Rank),它的框架就可以清空了 
	for(int i=1, u, v, z, fu, fv; i<=m; ++i){
		u=read(); v=read(); z=read(); fu=top[u]; fv=top[v];
		if(!kind.count(z)) kind[z]=++tot, wch[tot]=z;	// 用 map 來進行物品種類的離散化 
		z = kind[z]; 
		// 用圖儲存操作  
		while(fu!=fv){
			if(dep[fu]<dep[fv]){
				add_data(dfn[fv],z,1); add_data(dfn[v]+1,z,-1);
				v=fa[fv]; fv=top[v];
			}else{
				add_data(dfn[fu],z,1); add_data(dfn[u]+1,z,-1);
				u=fa[fu]; fu=top[u];
			}
		}
		if(dfn[u]>dfn[v]) swap(u,v);
		add_data(dfn[u],z,1); add_data(dfn[v]+1,z,-1);
	}
	build(1,tot,1);		// 建一棵維護物品數和種類的線段樹  
	for(int i=1, p; i<=n; ++i){
		for(p=head[i]; p; p=dt[p].next) change(1,tot,1,dt[p].z,dt[p].val);	// 讓整棵樹爲當前這個點服務  
		ans[Rank[i]] = nd[1].num ? nd[1].typ : 0;
	}
	for(int i=1; i<=n; ++i) printf("%d\n",wch[ans[i]]);
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章