• 洛谷 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;
    }
    
  • 相关阅读:
    Activiti学习笔记1 — 下载与开发环境的配置
    JavaScript实现本地图片上传前进行裁剪预览
    我国县及县级以上城市编码
    一些小技巧
    NodeJS学习之异步编程
    NodeJS学习之网络操作
    NodeJS学习之文件操作
    Sass和Compass设计师指南
    Sass
    CKEditor配置及使用
  • 原文地址:https://www.cnblogs.com/fusiwei/p/13820199.html
Copyright © 2020-2023  润新知