zoukankan      html  css  js  c++  java
  • [Vani有约会]雨天的尾巴(树上差分+线段树合并)

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

    Solution

    一看到链上操作,最后统计答案,自然而然的想到树上差分,a++ ,b++,lca--,fa[lca]--就可以完成一条链的操作。

    但这道题加的东西有好多种类。

    所以考虑对每个节点开一颗线段树,每次在对应位置加上。

    然后我们DFS的时候,朴素的树上差分直接size[u]+=size[v] (son[u]==v)这回u和v是两颗线段树。

    所以用线段数合并就行了。

    由于这题涉及大量的线段树,所以我们的数组得开大点。

    颜色需要提前离散化。

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #define N 100002
    using namespace std;
    int p[N][21],head[N],tot,deep[N],tr[N*50],L[N*50],R[N*50],num[N],rt[N],top,ans[N],toy;
    struct zzh{
      int n,to;
    }e[N<<1];
    struct bi{
        int a,b,d;
    }q[N];
    inline void add(int u,int v){
        e[++tot].n=head[u];
        e[tot].to=v;
        head[u]=tot;
    }
    void dfs(int u,int fa){
        for(int i=1;(1<<i)<=deep[u];++i)p[u][i]=p[p[u][i-1]][i-1];
        for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
            int v=e[i].to;p[v][0]=u;deep[v]=deep[u]+1;
            dfs(v,u);
        }
    }
    inline int getlca(int a,int b){
        if(deep[a]<deep[b])swap(a,b);
        for(int i=20;i>=0;--i)if(deep[a]-(1<<i)>=deep[b])a=p[a][i];
        if(a==b)return a;
        for(int i=20;i>=0;--i)if(p[a][i]!=p[b][i])a=p[a][i],b=p[b][i];
        return p[a][0];
    }
    void upd(int &cnt,int l,int r,int x,int y){
        if(!cnt)cnt=++toy;
        if(l==r){
            tr[cnt]+=y;
            return;
        }
        int mid=(l+r)>>1;
        if(mid>=x)upd(L[cnt],l,mid,x,y);
        else upd(R[cnt],mid+1,r,x,y);
        tr[cnt]=max(tr[L[cnt]],tr[R[cnt]]);
    }
    int query(int &cnt,int l,int r){    
        if(!tr[cnt]||!cnt)return 0;
        if(l==r)return num[l];
        int mid=(l+r)>>1;
        if(tr[L[cnt]]>=tr[R[cnt]])return query(L[cnt],l,mid);
        else return query(R[cnt],mid+1,r);
    }
    int merge(int &u,int v,int l,int r){
        if(!u||!v)return u+v;
        if(l==r){
            tr[u]+=tr[v];
            return u;
        }
        int mid=(l+r)>>1;
        L[u]=merge(L[u],L[v],l,mid);
        R[u]=merge(R[u],R[v],mid+1,r);
        tr[u]=max(tr[L[u]],tr[R[u]]);
        return u;
    }
    void dfs2(int u,int fa){
        for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
            int v=e[i].to;
            dfs2(v,u);
            rt[u]=merge(rt[u],rt[v],1,top);
        }
        ans[u]=query(rt[u],1,top);
    }
    int main(){
       int n,m,u,v;
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;++i)scanf("%d%d",&u,&v),add(u,v),add(v,u);
        dfs(1,0);
        for(int i=1;i<=m;++i)scanf("%d%d%d",&q[i].a,&q[i].b,&q[i].d),num[++top]=q[i].d;
        sort(num+1,num+top+1);
        top=unique(num+1,num+top+1)-num-1;
        for(int i=1;i<=m;++i){
            q[i].d=lower_bound(num+1,num+top+1,q[i].d)-num;
            int lca=getlca(q[i].a,q[i].b);
            upd(rt[q[i].a],1,top,q[i].d,1);upd(rt[q[i].b],1,top,q[i].d,1);
            upd(rt[lca],1,top,q[i].d,-1);upd(rt[p[lca][0]],1,top,q[i].d,-1);
        }
        dfs2(1,0);
        for(int i=1;i<=n;++i)printf("%d
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    【长篇高能】ReactiveCocoa 和 MVVM 入门
    圆形头像
    C#开发学习——.net C#中页面之间传值传参的方法以及内置对象
    C#开发学习——内联表达式
    C#开发学习——ADO.NET几个重要对象
    Android开发学习——动画
    Android开发学习—— Fragment
    Android开发学习—— ContentProvider内容提供者
    Android开发学习—— Service 服务
    Android开发学习—— Broadcast广播接收者
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/9668780.html
Copyright © 2011-2022 走看看