[BZOJ3307]:雨天的尾巴(LCA+树上差分+权值线段树)

题目传送门


题目描述

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


输入格式

第一行数字N,M
接下来N-1行,每行两个数字a,b,表示ab间有一条边
再接下来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≤109


题解

看到这道题我首先想到了这道题:BZOJ3631

对于上面这道题,如果你还没有思路的话可以看一下这篇博客:[BZOJ3631]:[JLOI2014]松鼠的新家(LCA+树上差分)

然后,我就立马想到了先求出LCA然后进行树上差分,但是这两道题的区别在于,这道题的物品种类不止一个,而且数还不小,显然不能单纯的用树上差分来解决。

于是考虑用权值线段树维护,先将物品种类排序,然后看利用树上差分更新权值线段树,之后递归合并统计答案即可。


代码时刻

#include<bits/stdc++.h>
using namespace std;
struct rec//存边
{
	int nxt;
	int to;
}e[200000];
struct qwq//存操作
{
	int x;
	int y;
	int z;
}question[100001]; 
int head[100001],cnt;
int fa[100001][20];
int root[100001],trfal[10000001],trsum[10000001],ls[10000001],rs[10000001],sum,tot;
int dfsv[100001];
int depth[100001];
int que[100001];
bool cmp(qwq a,qwq b){return a.z<b.z;}//按物品排序问题
void add(int x,int y)//建边
{
	e[++cnt].nxt=head[x];
	e[cnt].to=y;
	head[x]=cnt;
}
void dfs(int x)//dfs预处理父亲和深度
{
	dfsv[++dfsv[0]]=x;
    for(int i=head[x];i;i=e[i].nxt)
        if(e[i].to!=fa[x][0])
        {
	        fa[e[i].to][0]=x;
	        depth[e[i].to]=depth[x]+1;
	        for(int j=1;j<=18;j++)
				fa[e[i].to][j]=fa[fa[e[i].to][j-1]][j-1];
	        dfs(e[i].to);
        }
}
int LCA(int x,int y)//倍增求LCA
{
    if(depth[x]<depth[y])swap(x,y);
    for(int i=18;i>=0;i--)
        if(depth[fa[x][i]]>=depth[y])x=fa[x][i];
    if(x==y)return x;
    for(int i=18;i>=0;i--)
        if(fa[x][i]!=fa[y][i])
        {
            x=fa[x][i]; 
            y=fa[y][i];
        }
    return fa[x][0];
}
void pushup(int x)
{
    trfal[x]=max(trfal[ls[x]],trfal[rs[x]]);//维护的权值
    trsum[x]=(trfal[ls[x]]>=trfal[rs[x]])?trsum[ls[x]]:trsum[rs[x]];//哪种物品
}
void insert(int &x,int k,int fl,int l,int r)
{
    if(!x)x=++tot;
    if(l==r)
    {
        trfal[x]+=fl;
		trsum[x]=que[l];
        return;
    }
    int mid=(l+r)>>1;
    if(k<=mid)insert(ls[x],k,fl,l,mid);
    else insert(rs[x],k,fl,mid+1,r);
    pushup(x);
}
void ask(int &x,int fl,int l,int r)
{
    if(!fl)return;
    if(!x)
    {
        x=fl;
        return;
    }
    if(l==r)
    {
        trfal[x]+=trfal[fl];
        return;
    }
    int mid=(l+r)>>1;
    ask(ls[x],ls[fl],l,mid);
	ask(rs[x],rs[fl],mid+1,r);
    pushup(x);
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)root[i]=i;
	tot=n;
	for(int i=1;i<n;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		add(a,b);
		add(b,a);
	}
	depth[1]=1;
	dfs(1);
	for(int i=1;i<=m;i++)
		scanf("%d%d%d",&question[i].x,&question[i].y,&question[i].z);
	sort(question+1,question+m+1,cmp);
	for(int i=1;i<=m;i++)
	{
		int lca=LCA(question[i].x,question[i].y);
		if(question[i].z>question[i-1].z)que[++sum]=question[i].z;
		insert(root[question[i].x],sum,1,1,m);//树上差分操作
		insert(root[question[i].y],sum,1,1,m);
		insert(root[lca],sum,-1,1,m);
		if(lca!=1)insert(root[fa[lca][0]],sum,-1,1,m);
	}
    for(int i=n;i>1;i--)
		ask(root[fa[dfsv[i]][0]],root[dfsv[i]],1,m);
    for(int i=1;i<=n;i++)
		printf("%d\n",trsum[root[i]]);
	return 0;
}

rp++

posted @ 2019-06-23 12:04  HEOI-动动  阅读(234)  评论(0编辑  收藏  举报