• [Vani有约会]雨天的尾巴——树上差分+动态开点线段树合并


    题目描述

    首先村落里的一共有n座房屋,并形成一个树状结构。然后救济粮分m次发放,每次选择两个房屋(x,y),然后对于x到y的路径上(含x和y)每座房子里发放一袋z类型的救济粮。
    然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮。

    说明

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

    题解

    又是树上路径问题。

    要支持路径修改,并且是每个点是一个桶加入一个数。

    要支持最后的统一单点查询。

    树链剖分的话,显然对于桶的处理,怕不是要树套树了。总之打标记是一个很麻烦的事情。

    其实最后只要单点查询。

    所以,我们还可以树上差分。

    对于一般的树上差分,只是每个点有一个点权。

    但是,这个题每个点必须有一个桶。

    而且桶还得快速合并,并且支持查询出现最多的数

    桶还不能开满,因为会MLE

    所以,每个点开一个动态开点权值线段树即可。

    每次差分4个点,x,y,lca,fa[lca]

    最后,要像一般的树上差分一样dfs一遍

    不同的是,这个是线段树合并。

    merge函数(自己yy的):

    int merge(int x,int y){
        if(!x||!y) return x|y;
        if(!t[x].ls&&!t[y].ls&&!t[x].rs&&!t[y].rs){
            t[x].mx+=t[y].mx;
        }
        else{
        t[x].ls=merge(t[x].ls,t[y].ls);
        t[x].rs=merge(t[x].rs,t[y].rs);
        pushup(x);
        }
        return x;
    }

    或者更普遍的:

    int merge(int x,int y,int l,int r){
        if(!x||!y) return x|y;
        if(l==r){
            t[x].mx+=t[y].mx;
        }
        else{
        t[x].ls=merge(t[x].ls,t[y].ls,l,mid);
        t[x].rs=merge(t[x].rs,t[y].rs,mid+1,r);
        pushup(x);
        }
        return x;
    }

    总之就是叶子判断注意,必须暴力合并。

    复杂度?

    时间:本质就是merge函数启动次数,启动一次可以认为常数级别。

    成功merge一次,会删掉一个节点。如果不成功,直接return x|y也不会往下走了。

    开始有mlogn节点。所以复杂度最多mlogn

    空间:动态开点是最多mlogn*4,因为一次差分四点更新嘛。

    而之后的合并又不需要新的内存。

    可以赌一把,为了不MLE,开60*m也可以过。。。

    (这个题不能内存回收,否则还可以节省的。)

    bug:

    1.应该rt[x]=merge(rt[x],rt[y],1,n)  开始 没有rt[x]=返回值?dev luogu都没报错。。。

    2.线段树合并函数开始没有对叶子暴力处理。。。(毕竟还是第一次写)

    3.还有,线段树维护的是mx最大数个数,id最大数的值。

    如果t[rt[x]]的mx>0才行。因为我的程序只要差分有关,就把id记上了,虽然可能mx是-1。。。

    代码:

    #include<bits/stdc++.h>
    #define mid ((l+r)>>1)
    using namespace std;
    const int N=100000+2;
    const int U=100001;
    int n,m;
    struct haha{
        int nxt,to;
    }e[2*N];
    int hd[N],cnt;
    void add(int x,int y){
        e[++cnt].nxt=hd[x];
        e[cnt].to=y;hd[x]=cnt;
    }
    int fa[N][18];
    int dep[N];
    int ans[N];
    void dfs(int x,int d){//no fa
        dep[x]=d;
        for(int i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;if(y==fa[x][0]) continue;
            fa[y][0]=x;dfs(y,d+1);
        }
    }
    int lca(int x,int y){
        if(dep[x]<dep[y]) swap(x,y);
        for(int j=17;j>=0;j--){
            if(dep[fa[x][j]]>=dep[y]) x=fa[x][j];
        }
        if(x==y) return x;
        for(int j=17;j>=0;j--){
            if(fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
        }return fa[x][0];
    }
    struct node{
        int mx,id;
        int ls,rs;
    }t[N*60];
    int tot;
    int rt[N];
    void pushup(int x){
        if(t[x].ls&&t[x].rs){
            t[x].mx=max(t[t[x].ls].mx,t[t[x].rs].mx);
            t[x].id=t[t[x].ls].mx>=t[t[x].rs].mx?t[t[x].ls].id:t[t[x].rs].id;
        }
        else if(t[x].ls){
            t[x].mx=t[t[x].ls].mx;
            t[x].id=t[t[x].ls].id;
        }
        else if(t[x].rs){
            t[x].mx=t[t[x].rs].mx;
            t[x].id=t[t[x].rs].id;
        }
    }
    void upda(int &x,int l,int r,int p,int c){
        
        if(!x) x=++tot;
        //cout<<x<<" "<<l<<" "<<r<<" : "<<p<<" "<<c<<endl;
        if(l==r) {
            t[x].id=l,t[x].mx+=c;
            return;
        }
        if(p<=mid) upda(t[x].ls,l,mid,p,c);
        else upda(t[x].rs,mid+1,r,p,c);
        pushup(x);
    }
    int merge(int x,int y,int l,int r){
        if(!x||!y) return x|y;
        if(l==r){
            t[x].mx+=t[y].mx;
        }
        else{
        t[x].ls=merge(t[x].ls,t[y].ls,l,mid);
        t[x].rs=merge(t[x].rs,t[y].rs,mid+1,r);
        pushup(x);
        }
        return x;
    }
    void sol(int x){//no fa
        //cout<<" solving "<<x<<endl;
        for(int i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(y==fa[x][0]) continue;
            sol(y);
            //cout<<" rtx rty "<<rt[x]<<" "<<rt[y]<<endl;
            rt[x]=merge(rt[x],rt[y],1,U);
            //cout<<t[rt[x]].mx<<" "<<t[rt[x]].id<<endl;
        }
        if(t[rt[x]].mx>0)ans[x]=t[rt[x]].id;
    }
    int main()
    {
        scanf("%d%d",&n,&m);int x,y;
        for(int i=1;i<=n-1;i++){
            scanf("%d%d",&x,&y);add(x,y);add(y,x);
        }dfs(1,1);
        dep[0]=-1;
        for(int j=1;j<=17;j++)
            for(int i=1;i<=n;i++)
                 fa[i][j]=fa[fa[i][j-1]][j-1];
        int z;
        while(m--){
            scanf("%d%d%d",&x,&y,&z);
            int anc=lca(x,y);
            //cout<<" anc "<<anc<<" "<<fa[anc][0]<<endl;
            upda(rt[x],1,U,z,1);
            upda(rt[y],1,U,z,1);
            upda(rt[anc],1,U,z,-1);
            if(fa[anc][0])upda(rt[fa[anc][0]],1,U,z,-1);
            
        }
        /*for(int i=1;i<=n;i++){
            cout<<" rt "<<i<<" "<<rt[i]<<" : "<<t[rt[i]].mx<<" "<<t[rt[i]].id<<endl;
        }*/
        sol(1);
        for(int i=1;i<=n;i++){
            printf("%d
    ",ans[i]);
        }return 0;
    }

    总结:

    比较裸的树上差分

    然后比较裸的线段树合并。

    就是把树上差分的东西从值变成了物品。

    动态开点线段树+合并就应运而生了。

  • 相关阅读:
    salesforce零基础学习(八十七)Apex 中Picklist类型通过Control 字段值获取Dependent List 值
    salesforce lightning零基础学习(一) lightning简单介绍以及org开启lightning
    salesforce零基础学习(八十五)streaming api 简单使用(接近实时获取你需要跟踪的数据的更新消息状态)
    salesforce零基础学习(八十四)配置篇: 自定义你的home page layout
    salesforce零基础学习(八十三)analytics:reportChart实现Dashboard(仪表盘)功能效果
    salesforce零基础学习(八十二)审批邮件获取最终审批人和审批意见
    salesforce零基础学习(八十一)更改标准字段的label名称(Admin)
    salesforce零基础学习(八十)使用autoComplete 输入内容自动联想结果以及去重实现
    salesforce零基础学习(七十九)简单排序浅谈 篇一
    第三百五十一节,Python分布式爬虫打造搜索引擎Scrapy精讲—将selenium操作谷歌浏览器集成到scrapy中
  • 原文地址:https://www.cnblogs.com/Miracevin/p/9670596.html
Copyright © 2020-2023  润新知