BZOJ3307雨天的尾巴——线段树合并

时间:2021-11-13 20:21:19

题目描述

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

输入

第一行数字N,M
接下来N-1行,每行两个数字a,b,表示a与b间有一条边
再接下来M行,每行三个数字x,y,z.如题

输出

输出有N行
每i行的数字表示第i个点存放最多的物品是哪一种,如果有
多种物品的数量一样,输出编号最小的。如果某个点没有物品
则输出0

样例输入

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

样例输出

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
 
线段树合并好题。
考虑对于一次操作可以看作是将x到根和y到根路径上每个点加一个z物品,将x,y的lca和lca的父亲到根路径上每个点减一个z物品,也就是树上差分。
因为要维护每个点物品数最多的物品是什么,在每一个点动态开点建一棵权值线段树维护这个点的每种物品数量。
每次操作树上差分一下,在对应点的的权值线段树上修改。
那么一个点维护每种物品数的线段树就是它子树中每个点的线段树合并后的树。
因此从根节点dfs,回溯时将每个点的子树权值线段树合并到这个点的线段树上并查询物品数最多的物品就好了。
因为每次操作相当于在一棵线段树上新建出一条链(实际上是在四棵树上各建一条),那么合并就相当于将4m条链合并,每两条链合并是O(logn),总时间复杂度就是O(mlogn)。
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int h[100010];
int root[100010];
int ls[6000010];
int rs[6000010];
int next[200010];
int head[100010];
int to[200010];
int f[100010][18];
int d[100010];
int sum[6000010];
int ans[100010];
int tot;
int x,y;
int n,m;
int num;
int cnt;
struct miku
{
int x;
int y;
int z;
}a[100010];
void add(int x,int y)
{
tot++;
next[tot]=head[x];
head[x]=tot;
to[tot]=y;
}
void dfs(int x)
{
d[x]=d[f[x][0]]+1;
for(int i=1;i<=17;i++)
{
f[x][i]=f[f[x][i-1]][i-1];
}
for(int i=head[x];i;i=next[i])
{
if(to[i]!=f[x][0])
{
f[to[i]][0]=x;
dfs(to[i]);
}
}
}
int lca(int x,int y)
{
if(d[x]<d[y])
{
swap(x,y);
}
int dep=d[x]-d[y];
for(int i=0;i<=17;i++)
{
if((dep&(1<<i)))
{
x=f[x][i];
}
}
if(x==y)
{
return x;
}
for(int i=17;i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
void updata(int rt)
{
if(ls[rt]&&rs[rt])
{
sum[rt]=max(sum[ls[rt]],sum[rs[rt]]);
}
else if(ls[rt])
{
sum[rt]=sum[ls[rt]];
}
else if(rs[rt])
{
sum[rt]=sum[rs[rt]];
}
}
void insert(int &rt,int l,int r,int k,int v)
{
if(!rt)
{
rt=++cnt;
}
if(l==r)
{
sum[rt]+=v;
return ;
}
int mid=(l+r)>>1;
if(k<=mid)
{
insert(ls[rt],l,mid,k,v);
}
else
{
insert(rs[rt],mid+1,r,k,v);
}
updata(rt);
}
void merge(int &rt,int x)
{
if(!rt||!x)
{
rt=rt+x;
return ;
}
sum[rt]+=sum[x];
merge(ls[rt],ls[x]);
merge(rs[rt],rs[x]);
updata(rt);
}
int query(int rt,int l,int r)
{
int mid=(l+r)>>1;
if(l==r)
{
if(sum[rt]>0)
{
return l;
}
return 0;
}
if(sum[ls[rt]]>=sum[rs[rt]])
{
return query(ls[rt],l,mid);
}
else
{
return query(rs[rt],mid+1,r);
}
}
void downdata(int x)
{
for(int i=head[x];i;i=next[i])
{
if(to[i]!=f[x][0])
{
downdata(to[i]);
merge(root[x],root[to[i]]);
}
}
ans[x]=query(root[x],1,num);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
h[i]=a[i].z;
}
sort(h+1,h+1+m);
num=unique(h+1,h+1+m)-h-1;
for(int i=1;i<=m;i++)
{
int anc=lca(a[i].x,a[i].y);
a[i].z=lower_bound(h+1,h+1+num,a[i].z)-h;
insert(root[a[i].x],1,num,a[i].z,1);
insert(root[a[i].y],1,num,a[i].z,1);
insert(root[anc],1,num,a[i].z,-1);
if(anc!=1)
{
insert(root[f[anc][0]],1,num,a[i].z,-1);
}
}
downdata(1);
for(int i=1;i<=n;i++)
{
printf("%d\n",h[ans[i]]);
}
}