zoukankan      html  css  js  c++  java
  • 洛谷P4556 雨天的尾巴

    题目链接: P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并
    题目大意:
    有一颗 (n) 个节点的树,(m) 次操作,每次将节点 (u)(v) 的路径上的每个点放一个物品 (c) ,最后询问每个节点上数量最多的物品是什么,其中数量相同的物品取编号最小者,若无物品输出0。
    (n,m,cleq 10^5)
    思路:
    如题目名称,模板题,蒟蒻之前没写过线段树合并,就简单总结一下吧。
    线段树合并有两种写法:
    1.将 (b) 直接合并到 (a) 上去:

    int merge(int a,int b){
        if(!a)return b;
        if(!b)return a;
        if(t[a].l==t[a].r){
            //对线段树储存信息的叶节点进行具体的合并
        }else{
            t[a].ls=merge(t[a].ls,t[b].ls);
            t[a].rs=merge(t[a].rs,t[b].rs);
            update(a);
        }
        return a;
    }
    

    这种写法会使 (a) 直接继承了部分 (b) 的节点,所以之后对 (a) 进行其他的修改时,(b) 也会被改掉,因此这么写只适用与离线查询(如此题)。

    2.新建节点储存合并后的树

    int merge(int a,int b){
        if(!a)return b;
        if(!b)return a;
        int root=++cnt;
        if(t[a].l==t[a].r){
            t[root]=....;//进行具体的信息合并
        }else{
            t[root].ls=merge(t[a].ls,t[b].ls);
            t[root].rs=merge(t[a].rs,t[b].rs);
            update(root);
        }
        return root;
    }
    

    这样写就可以适用于动态查询,其缺点是空间复杂度比较大,在离线查询时线段树的空间约为前者的两倍。


    对于这道题,我们考虑树上差分,对于每一个节点建一颗动态开点权值线段树,然后dfs时节点和自己的所有儿子合并即可。

    细节:

    • 如果写的是第一种合并方式,那么每个节点需要在回溯前将答案保存到 (ans_i) 中,因为之后该节点上的线段树可能会被其祖先修改。
    • 尝试将 (Tarjan)(LCA)(dfs) 写到一起是一件很荒谬的事情,计算答案的先后顺序会出大问题。
    • 日常:当 (LCA)(a)(b) 是同一个节点时,只记录一次。

    Code:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<fstream>
    #define N 100100
    #define R 100000
    using namespace std;
    inline int read(){
        int s=0,w=1;
        char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
        return s*w;
    }
    struct tree{
        int l,r;
        int mx,node;
        int ls,rs;
    }t[17*4*N];
    int head[N],to[N*2],nxt[N*2];
    int cnt_n,cnt_e;
    int root[N];
    int ancest[N],fa[N];
    int ans[N];
    vector<pair<int,int> > upd[N];
    bool vis[N];
    void init(int n){
        cnt_e=-1;
        memset(head,-1,sizeof(head));
        for(int i=1;i<=n;i++)fa[i]=i;
    }
    void add_e(int a,int b,bool id){
        nxt[++cnt_e]=head[a];
        head[a]=cnt_e;
        to[cnt_e]=b;
        if(id)add_e(b,a,0);
    }
    void update(int x){
        int a=t[t[x].ls].mx,b=t[t[x].rs].mx;
        int c=t[t[x].ls].node,d=t[t[x].rs].node;
        t[x].mx=max(a,b);
        if(t[x].rs==0||a>b||(a==b&&c<d))t[x].node=c;
        else t[x].node=d;
    }
    void insert(int &a,int l,int r,int pos,int k){
        if(!a)a=++cnt_n,t[a].l=l,t[a].r=r;
        if(l==r){
            t[a].mx+=k,t[a].node=l;return;
        }
        int mid=(l+r)>>1;
        if(mid>=pos)insert(t[a].ls,l,mid,pos,k);
        else insert(t[a].rs,mid+1,r,pos,k);
        update(a);
    }
    int find(int x){
        if(fa[x]==x)return x;
        return fa[x]=find(fa[x]);
    }
    int merge(int a,int b){
        if(!a)return b;
        if(!b)return a;
        int l=t[a].l,r=t[a].r;
        if(l==r){
            t[a].mx+=t[b].mx;
        }else{
            t[a].ls=merge(t[a].ls,t[b].ls);
            t[a].rs=merge(t[a].rs,t[b].rs);
            update(a);
        }
        return a;
    }
    void tarjan(int x,int fath){
        ancest[x]=fath;
        for(int i=head[x];~i;i=nxt[i]){
            if(vis[to[i]]||to[i]==fath)continue;
            tarjan(to[i],x);
        }
        vis[x]=true;
        for(int i=upd[x].size()-1;i>=0;i--){
            int y=upd[x][i].first,z=upd[x][i].second;
            if(vis[y]){
                insert(root[y],1,R,z,1);
                insert(root[x],1,R,z,1);
                int lca=find(y);
                insert(root[lca],1,R,z,-1);
                insert(root[ancest[lca]],1,R,z,-1);
            }
        }
        fa[x]=fath;
    }
    void dfs(int x){
        for(int i=head[x];~i;i=nxt[i]){
            if(to[i]==ancest[x])continue;
            dfs(to[i]);
            root[x]=merge(root[x],root[to[i]]);
        }
        ans[x]=t[root[x]].mx>0?t[root[x]].node:0;
    }
    int main(){
        int n,m;
        int x,y,z;
        cin>>n>>m;
        init(n);
        for(int i=1;i<n;i++){
            add_e(read(),read(),1);
        }
        for(int i=0;i<m;i++){
            x=read(),y=read(),z=read();
            upd[x].push_back(make_pair(y,z));
            if(x!=y)upd[y].push_back(make_pair(x,z));
        }
        tarjan(1,0);
        dfs(1);
        for(int i=1;i<=n;i++)
            printf("%d
    ",ans[i]);
        return 0;
    }
    
  • 相关阅读:
    Oracle EXP
    Using Spring in Web and WinForms
    System.ComponentModel(未完...)
    工作必须得到强势方的支持!
    book.Save()还是bookManager.Save(book)?
    C#中常用的Attribute搜集(刚开始...)
    领域模型是否能够屏蔽数据库?
    合格代码的最基本的标准
    关于配置系统的设计
    对象分类
  • 原文地址:https://www.cnblogs.com/Neal-lee/p/14254434.html
Copyright © 2011-2022 走看看