3307: 雨天的尾巴
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 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
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
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
-------------------------------------------------------------------------------
算法:
树链剖分 + 树上差分 + 线段树
做法:
如果只在一个数列上进行 [ 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]]);
}