zoukankan      html  css  js  c++  java
  • 洛谷 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;
    }
    
  • 相关阅读:
    波段是金牢记六大诀窍
    zk kafka mariadb scala flink integration
    Oracle 体系结构详解
    图解 Database Buffer Cache 内部原理(二)
    SQL Server 字符集介绍及修改方法演示
    SQL Server 2012 备份与还原详解
    SQL Server 2012 查询数据库中所有表的名称和行数
    SQL Server 2012 查询数据库中表格主键信息
    SQL Server 2012 查询数据库中所有表的索引信息
    图解 Database Buffer Cache 内部原理(一)
  • 原文地址:https://www.cnblogs.com/fusiwei/p/13820199.html
Copyright © 2011-2022 走看看