洛谷 P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并

洛谷 P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并

洛谷传送门

题目背景

深绘里一直很讨厌雨天。

灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。

虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉。

无奈的深绘里和村民们只好等待救济粮来维生。

不过救济粮的发放方式很特别。

题目描述

首先村落里的一共有 nn 座房屋,并形成一个树状结构。然后救济粮分 mm 次发放,每次选择两个房屋 (x,~y)(x, y),然后对于 xx 到 yy 的路径上(含 xx 和 yy)每座房子里发放一袋 zz 类型的救济粮。

然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮。

输入格式

输入的第一行是两个用空格隔开的正整数,分别代表房屋的个数 nn 和救济粮发放的次数 mm

第 22 到 第 nn 行,每行有两个用空格隔开的整数 a,~ba, b,代表存在一条连接房屋 aa 和 bb 的边。

第 (n + 1)(n+1) 到第 (n + m)(n+m) 行,每行有三个用空格隔开的整数 x,~y,~zx, y, z,代表一次救济粮的发放是从 xx 到 yy 路径上的每栋房子发放了一袋 zz 类型的救济粮。

输出格式

输出 nn 行,每行一个整数,第 ii 行的整数代表 ii 号房屋存放最多的救济粮的种类,如果有多种救济粮都是存放最多的,输出种类编号最小的一种。

如果某座房屋没有救济粮,则输出 00。


题解:

一开始考虑的是类比SDOI2014旅行的做法,对于(z)颜色每种颜色都开一棵动态开点线段树。但是这道题需要维护的东西却不能通过这种做法很好地维护出来。怎么办呢?

这种维护种类编号的题,我们可以考虑用权值线段树来维护。这样的话,我们在每个线段树节点维护两个信息,一个是最大值,一个是最大值编号,就可以很方便地来维护。

但是这样的话,我们需要在每个节点开一棵权值线段树,如果我们暴力地用树剖序去更新权值线段树的话,复杂度肯定是受不了。所以我们进一步观察性质:

很多次修改,一次查询。

差分啊!

所以我们的每次询问只需要用树上差分,只需要有4次操作即可。

最后的树上差分统计答案,因为在每个节点运用了权值线段树,所以要有权值线段树的合并。

关于线段树合并:

浅谈线段树合并

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
char *p1,*p2,buf[100000];
#define nc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int read()
{
    int x=0,f=1;
    char ch=nc();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            f=-1;
        ch=nc();
    }
    while(ch>='0'&&ch<='9')
        x=x*10+ch-'0',ch=nc();
   	return x*f;
}
const int maxn=1e5+10;
int n,m,maxx;
int tot,to[maxn<<1],nxt[maxn<<1],head[maxn];
int deep[maxn],size[maxn],fa[maxn],top[maxn],son[maxn],root[maxn];
int lson[maxn*60],rson[maxn*60],sum[maxn*60],wh[maxn*60],cnt;
//sum存最多救济粮个数,wh存最小编号的最多救济粮种类
int ans[maxn];
struct node
{
    int x,y,z;
}q[maxn];
void add(int x,int y)
{
    to[++tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
}
void dfs1(int x,int f)
{
    deep[x]=deep[f]+1;
    fa[x]=f;
    size[x]=1;
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        if(y==f)
            continue;
        dfs1(y,x);
        size[x]+=size[y];
        if(!son[x]||size[y]>size[son[x]])
            son[x]=y;
    }
}
void dfs2(int x,int t)
{
    top[x]=t;
    if(!son[x])
        return;
    dfs2(son[x],t);
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        if(y==fa[x]||y==son[x])
            continue;
        dfs2(y,y);
    }
}
int lca(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(deep[top[x]]<deep[top[y]])
            swap(x,y);
        x=fa[top[x]];
    }
    return deep[x]<deep[y]?x:y;
}
void pushup(int pos)
{
    if(sum[lson[pos]]>=sum[rson[pos]])
        sum[pos]=sum[lson[pos]],wh[pos]=wh[lson[pos]];
    else
        sum[pos]=sum[rson[pos]],wh[pos]=wh[rson[pos]];
}
void update(int &pos,int l,int r,int x,int k)//将数x的个数+k
{
    int mid=(l+r)>>1;
    if(!pos)
        pos=++cnt;   
    {
        sum[pos]+=k;
        wh[pos]=l;
        return;
    }
    if(x<=mid)
        update(lson[pos],l,mid,x,k);
    else
        update(rson[pos],mid+1,r,x,k);
    pushup(pos);
}
void merge(int &x,int y,int l,int r)
{
    int mid=(l+r)>>1;
    if(!x||!y)
    {
        x=(!x?y:x);
        return;
    }
    if(l==r)
    {
        sum[x]+=sum[y];
        wh[x]=l;
        return;
    }
    merge(lson[x],lson[y],l,mid);
    merge(rson[x],rson[y],mid+1,r);
    pushup(x);
}
void dfs(int x)
{
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        if(y==fa[x])
            continue;
        dfs(y);
        merge(root[x],root[y],1,maxx);
    }
    if(sum[root[x]])
        ans[x]=wh[root[x]];
}
int main()
{
    n=read();m=read();
    for(int i=1;i<n;i++)
    {
        int x,y;
        x=read();y=read();
        add(x,y);
        add(y,x);
    }
    dfs1(1,0);
    dfs2(1,1);
    for(int i=1;i<=m;i++)
    {
        q[i].x=read(),q[i].y=read(),q[i].z=read();
        maxx=max(maxx,q[i].z);
    }
    for(int i=1;i<=m;i++)//树上的每一个节点有一棵权值线段树
    {
        int l=lca(q[i].x,q[i].y);
        update(root[q[i].x],1,maxx,q[i].z,1);
        update(root[q[i].y],1,maxx,q[i].z,1);
        update(root[l],1,maxx,q[i].z,-1);
        if(fa[l])
            update(root[fa[l]],1,maxx,q[i].z,-1);
    }
    dfs(1);
    for(int i=1;i<=n;i++)
        printf("%d
",ans[i]);
    return 0;
}
原文地址:https://www.cnblogs.com/fusiwei/p/13820199.html