P4556 [Vani有约会]雨天的尾巴 树链剖分 线段树合并

P4556 [Vani有约会]雨天的尾巴

提交2.75k

通过789

时间限制1.00s

内存限制125.00MB

提交代码加入收藏

题目提供者yyy2015c01

难度省选/NOI-

历史分数100

提交记录查看题解

标签

查看算法标签

相关讨论

进入讨论版

查看讨论

推荐题目

查看推荐

展开

题目背景

深绘里一直很讨厌雨天。
灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。
虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉。
无奈的深绘里和村民们只好等待救济粮来维生。
不过救济粮的发放方式很特别。

题目描述

首先村落里的一共有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。

输入输出样例

输入 #1复制

5 3
1 2
3 1
3 4
5 3
2 3 3
1 5 2
3 3 3

输出 #1复制

2
3
3
0
2

说明/提示

对于20%的数据,1 <= n, m <= 100
对于50%的数据,1 <= n, m <= 2000
对于100%的数据,1 <= n, m <= 100000, 1 <= a, b, x, y <= n, 1 <= z <= 100000

这题很明显是一道树剖题 但是可以用线段树合并来解决

显然每个点维护一个权值线段树  可以直接输出做多的粮食是什么

因为线段树合并自下而上类似一个求前缀和的过程 所以可以利用查分数字 点更新

在u+1 v+1 lca(u,v)-1 falca(u,v)-1  即可

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i>=(b);--i)
#define ll long long
#define see(x) (cerr<<(#x)<<‘=‘<<(x)<<endl)
#define inf 0x3f3f3f3f
#define CLR(A,v)  memset(A,v,sizeof A)
//////////////////////////////////
const int N=6000005;

int T[N<<2],lson[N<<2],rson[N<<2],maxx[N<<2],t[N<<2],ncnt,n,m,nn,ans[N];
void up(int pos)
{
    if(maxx[lson[pos]]>=maxx[rson[pos]])
    maxx[pos]=maxx[lson[pos]],t[pos]=t[lson[pos]];
    else maxx[pos]=maxx[rson[pos]],t[pos]=t[rson[pos]];
}
void upnode(int x,int v,int l,int r,int &pos)
{
    if(!pos)pos=++ncnt;
    if(l==r){t[pos]=l;maxx[pos]+=v;return ;}
    int m=(l+r)>>1;
    if(x<=m)upnode(x,v,l,m,lson[pos]);
    else upnode(x,v,m+1,r,rson[pos]);
    up(pos);
}
int Merge(int a,int b,int l,int r)
{
    if(!a)return b;
    if(!b)return a;
    if(l==r)
    {
        maxx[a]+=maxx[b];
        t[a]=l;
        return a;
    }
    int m=(l+r)>>1;
    lson[a]=Merge(lson[a],lson[b],l,m);
    rson[a]=Merge(rson[a],rson[b],m+1,r);
    up(a);
    return a;
}
////////////////////////
int fa[N],top[N],siz[N],son[N],dep[N],pos,head[N];
struct Edge{int to,nex;}edge[N];
void add(int a,int b){edge[++pos]=(Edge){b,head[a]};head[a]=pos;}

void dfs1(int x,int f)
{
    fa[x]=f;dep[x]=dep[f]+1;siz[x]=1;son[x]=0;
    for(int i=head[x];i;i=edge[i].nex)
    {
        int v=edge[i].to;if(v==f)continue;
        dfs1(v,x);siz[x]+=siz[v];
        if(siz[son[x]]<siz[v])son[x]=v;
    }
}
void dfs2(int x,int topf)
{
    top[x]=topf;
    if(son[x])dfs2(son[x],topf);
    for(int i=head[x];i;i=edge[i].nex)
    {
        int v=edge[i].to;if(v==fa[x]||v==son[x])continue;
        dfs2(v,v);
    }
}
int Lca(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        x=fa[top[x]];
    }
    return dep[x]<dep[y]?x:y;
}
void dfs3(int x)
{
    for(int i=head[x];i;i=edge[i].nex)if(dep[edge[i].to]>dep[x])
    dfs3(edge[i].to),T[x]=Merge(T[x],T[edge[i].to],1,nn);
    if(maxx[T[x]])ans[x]=t[T[x]];
}
int L[N],R[N],V[N];
int main()
{
    cin>>n>>m;
    rep(i,1,n-1)
    {
        int x,y;scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    rep(i,1,n)T[i]=i,++ncnt;
    dfs1(1,0);
    dfs2(1,1);
    rep(i,1,m)
    {
        scanf("%d%d%d",&L[i],&R[i],&V[i]);
        nn=max(nn,V[i]);
    }
    rep(i,1,m)
    {
        int lca=Lca(L[i],R[i]);
        upnode(V[i],1,1,nn,T[L[i]]);
        upnode(V[i],1,1,nn,T[R[i]]);
        upnode(V[i],-1,1,nn,T[lca]);
        if(fa[lca])upnode(V[i],-1,1,nn,T[fa[lca]]);
    }
    dfs3(1);
    rep(i,1,n)
    printf("%d\n",ans[i]);

    return 0;
}

原文地址:https://www.cnblogs.com/bxd123/p/11383715.html

时间: 2024-10-06 01:14:58

P4556 [Vani有约会]雨天的尾巴 树链剖分 线段树合并的相关文章

LG4556 [Vani有约会]雨天的尾巴 动态开点线段树+线段树合并

问题描述 LG4556 题解 对于每一个结点,建立一棵动态开点线段树. 然后自低向上合并线段树. 同时维护整个值域的最大值和最大值位置. \(\mathrm{Code}\) #include<bits/stdc++.h> using namespace std; template <typename Tp> void read(Tp &x){ x=0;char ch=1;int fh; while(ch!='-'&&(ch>'9'||ch<'0'

Aizu 2450 Do use segment tree 树链剖分+线段树

Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show.php?pid=39566 Description Given a tree with n (1 ≤ n ≤ 200,000) nodes and a list of q (1 ≤ q ≤ 100,000) queries, process the queries in order and out

Hdu 3966 Aragorn&#39;s Story (树链剖分 + 线段树区间更新)

题目链接: Hdu 3966 Aragorn's Story 题目描述: 给出一个树,每个节点都有一个权值,有三种操作: 1:( I, i, j, x ) 从i到j的路径上经过的节点全部都加上x: 2:( D, i, j, x ) 从i到j的路径上经过的节点全部都减去x: 3:(Q, x) 查询节点x的权值为多少? 解题思路: 可以用树链剖分对节点进行hash,然后用线段树维护(修改,查询),数据范围比较大,要对线段树进行区间更新 1 #include <cstdio> 2 #include

【bzoj3589】动态树 树链剖分+线段树

题目描述 别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件 事件0:这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子. 事件1:小明希望你求出几条树枝上的果子数. 一条树枝其实就是一个从某个节点到根的路径的一段. 每次小明会选定一些树枝, 让你求出在这些树枝上的节点的果子数的和. 注意, 树枝之间可能会重合, 这时重合的部分的节点的果子只要算一次. 输入 第一行一个整数n(1<=n<=200,000), 即节点数. 接下来n-1行, 每行两个数字u,

BZOJ2243 (树链剖分+线段树)

Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. 解题分析 树链剖分+线段树. 开一个记录类型,记录某一段区间的信息.l 表示区间最左侧的颜色 , r 表示区间最右侧的颜色 , sum 表示区间中颜色段数量. 合并时判断一下左区间的右端点和有区间的左端点的颜色是否一样. 树上合并时需要用两个变量ans1,ans2来存储.ans1表示x往上走时形成的链的信息,

bzoj4304 (树链剖分+线段树)

Problem T2 (bzoj4304 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 解题分析 练手题.树链剖分+线段树. 参考程序 1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #incl

【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)

第一种做法(时间太感人): 这题我真的逗了,调了一下午,疯狂造数据,始终找不到错. 后来发现自己sb了,更新那里没有打id,直接套上u了.我.... 调了一下午啊!一下午的时光啊!本来说好中午A掉去学习第二种做法,噗 好吧,现在第一种做法是hld+seg+bst+二分,常数巨大,log^4级别,目前只会这种. 树剖后仍然用线段树维护dfs序区间,然后在每个区间建一颗平衡树,我用treap,(这题找最大啊,,,囧,并且要注意,这里的rank是比他大的数量,so,我们在二分时判断要判断一个范围,即要

【bzoj4811】[Ynoi2017]由乃的OJ 树链剖分+线段树区间合并

题目描述 由乃正在做她的OJ.现在她在处理OJ上的用户排名问题.OJ上注册了n个用户,编号为1-",一开始他们按照编号 排名.由乃会按照心情对这些用户做以下四种操作,修改用户的排名和编号:然而由乃心情非常不好,因为Deus天 天问她题...因为Deus天天问由乃OI题,所以由乃去学习了一下OI,由于由乃智商挺高,所以OI学的特别熟练她 在RBOI2016中以第一名的成绩进入省队,参加了NOI2016获得了金牌保送 Deus:这个题怎么做呀? yuno:这个不是NOI2014的水题吗... Deu

HDU 2460 Network(双连通+树链剖分+线段树)

HDU 2460 Network 题目链接 题意:给定一个无向图,问每次增加一条边,问个图中还剩多少桥 思路:先双连通缩点,然后形成一棵树,每次增加一条边,相当于询问这两点路径上有多少条边,这个用树链剖分+线段树处理 代码: #include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; #pragma comment(linke