zoukankan      html  css  js  c++  java
  • [线段树][树上差分] Jzoj P3397 雨天的尾巴

    Description

    深绘里一直很讨厌雨天。

    灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。

    虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连

    根拔起,以及田地里的粮食被弄得一片狼藉。

    无奈的深绘里和村民们只好等待救济粮来维生。

    不过救济粮的发放方式很特别。

    首先村落里的一共有n 座房屋,并形成一个树状结构。然后救济粮分m 次发放,每次选择

    两个房屋(x,y),然后对于x 到y 的路径上(含x 和y) 每座房子里发放一袋z 类型的救济粮。

    然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮。

    Input

    第一行两个正整数n,m,含义如题目所示。

    接下来n-1 行,每行两个数(a,b),表示(a,b) 间有一条边。

    再接下来m 行,每行三个数(x,y,z),含义如题目所示。

    Output

    n 行,第i 行一个整数,表示第i 座房屋里存放的最多的是哪种救济粮,如果有多种救济粮

    存放次数一样,输出编号最小的。

    如果某座房屋里没有救济粮,则对应一行输出0。

    Sample Input

    5 3
    1 2
    3 1
    3 4
    5 4
    2 3 3
    1 5 2
    3 3 3

    Sample Output

    2
    3
    3
    2
    2

    Data Constraint

    对于20% 的数据,1<= n,m <= 100

    对于50% 的数据,1 <= n,m <= 2000

    对于100% 的数据,1 <= n;m <= 100000; 1 <= a, b, x, y <= n; 1 <= z <= 10^9

    题解

    • 考虑如果是在一条链上的话要怎么做
    • 那么我们可以在x处加一个加入z的标记,在y+1出加入一个删掉z的标记,然后用权值线段树扫一遍就好了
    • 现在拓展到一棵树上,我们可以在x和y处加入一个加上z的标记,在lca和fa[lca]处加入一个删除z的标记,那么我们每次可以把子节点的线段树合并上来
    • 然后处理标记信息,最后查询答案即可

    代码

     1 #include <cstdio>
     2 #include <iostream>
     3 #include <cstring>
     4 #include <vector>
     5 #define N 100010
     6 using namespace std;
     7 struct tree { int l,r,sum,v; }t[N*50];
     8 int n,m,cnt,deep[N],a[N],root[N],fa[N][18],ans[N];
     9 vector<int>e[N],p[N],q[N];
    10 void pushup(int x) { if (t[t[x].r].sum>t[t[x].l].sum) t[x].sum=t[t[x].r].sum,t[x].v=t[t[x].r].v; else t[x].sum=t[t[x].l].sum,t[x].v=t[t[x].l].v; }
    11 void dfs(int x,int f,int dep)
    12 {
    13     fa[x][0]=f,deep[x]=dep,root[x]=x,cnt++;
    14     for (int i=1;i<=17;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
    15     for (int i=0;i<e[x].size();i++) if (e[x][i]!=f) dfs(e[x][i],x,dep+1);    
    16 }
    17 int LCA(int x,int y)
    18 {
    19     if (deep[x]<deep[y]) swap(x,y);
    20     for (int i=17;i>=0;i--) if (deep[fa[x][i]]>=deep[y]) x=fa[x][i];
    21     if (x==y) return x;
    22     for (int i=17;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    23     return fa[x][0];
    24 }
    25 int update(int &d,int l,int r,int x,int y)
    26 {
    27     if (!d) d=++cnt;
    28     if (l==r) { t[d].sum+=y,t[d].v=l; return 0; }
    29     int mid=l+r>>1;
    30     if (x<=mid) update(t[d].l,l,mid,x,y); else update(t[d].r,mid+1,r,x,y);
    31     pushup(d);
    32 }
    33 int merge(int x,int y,int l,int r)
    34 {
    35     if (!x) return y;
    36     if (!y) return x;
    37     if (l==r) { t[x].sum+=t[y].sum,t[x].v=l; return x; }
    38     int mid=l+r>>1;
    39     t[x].l=merge(t[x].l,t[y].l,l,mid),t[x].r=merge(t[x].r,t[y].r,mid+1,r);
    40     pushup(x); return x;
    41 }
    42 void dfs1(int x,int pre)
    43 {
    44     for (int i=0;i<e[x].size();i++) if (e[x][i]!=pre) dfs1(e[x][i],x),merge(root[x],root[e[x][i]],1,N-10);
    45     for (int i=0;i<p[x].size();i++) update(root[x],1,N-10,p[x][i],1);
    46     for (int i=0;i<q[x].size();i++) update(root[x],1,N-10,q[x][i],-1);
    47     ans[x]=t[root[x]].v;
    48 }
    49 int main()
    50 {
    51     scanf("%d%d",&n,&m);
    52     for (int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),e[x].push_back(y),e[y].push_back(x);
    53     dfs(1,0,1);
    54     for (int i=1,x,y,z;i<=m;i++)
    55     {
    56         scanf("%d%d%d",&x,&y,&z);
    57         int lca=LCA(x,y);
    58         p[x].push_back(z),p[y].push_back(z),q[lca].push_back(z);
    59         if (fa[lca][0]) q[fa[lca][0]].push_back(z); 
    60     }
    61     dfs1(1,0);
    62     for (int i=1;i<=n;i++) printf("%d
    ",ans[i]);
    63 }
  • 相关阅读:
    使用线程池对应用程序产生线程个数与占用CPU时间的影响
    枚举类型解析
    性能监视器的简单使用
    WCF 承载服务
    ManulResetEvent与AutoResetEvent
    获取当前目录的上级目录
    WCF 异步调用
    适配器设计模式
    控制控制台自身的最大化最小化
    代码自动生成技术相关介绍
  • 原文地址:https://www.cnblogs.com/Comfortable/p/11176223.html
Copyright © 2011-2022 走看看