[洛谷P4556] 雨天的尾巴

这道题可以用线段树合并做,网上的题解基本上都是线段树合并的。

但是为什么我就偏偏要用dsu on tree......

题目传送门

dsu on tree的方法类似[CF1009F] Dominant Indices(这是我之前写的题解)。

但是这道题要把树链操作变成某点到根的操作,最后统计子树贡献和。

子树贡献有正有负,不能像那道题那样用堆维护最大值(在这卡了半天)。

因为那道题只有加,这道题有加有减。

可能加上之后又减掉了,但是堆给出的最大值会是减掉之前的值(因为值更大)。

所以用线段树维护一下最大值。

这个线段树是比较简单的,支持单点修改即可。

查询时必然查询的是0~n的全局最大值。

所以就是线段树根的最大值喽。

注意线段树必须有一个0的位置。

因为如果没有救济粮的话要输出0。

至于如果有多种救济粮存放次数一样,怎么输出编号最小的。

在线段树向上更新的时候:(具体见代码第82行)

要这么写:if(mv[num<<1]>=mv[num<<1|1])

不能这么写:if(mv[num<<1]>mv[num<<1|1])

这样可以保证如果两边的最大值一样,会用左边的(也就是编号较小的)来更新。

怕卡,lca用的树剖。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<queue>
  5 using namespace std;
  6 
  7 int n,m;
  8 int hd[100005],nx[200005],to[200005],ec;
  9 int fir[100005],bes[400005],kd[400005],fl[400005],qc;
 10 int f[100005],son[100005],sz[100005],d[100005],tp[100005];
 11 
 12 void edge(int af,int at)
 13 {
 14     to[++ec]=at;
 15     nx[ec]=hd[af];
 16     hd[af]=ec;
 17 }
 18 
 19 void mark(int p,int pkd,int pfl)
 20 {
 21     kd[++qc]=pkd;
 22     fl[qc]=pfl;
 23     bes[qc]=fir[p];
 24     fir[p]=qc;
 25 }
 26 
 27 void pre(int p,int fa)
 28 {
 29     f[p]=fa;
 30     d[p]=d[fa]+1;
 31     sz[p]=1;
 32     for(int i=hd[p];i;i=nx[i])
 33     {
 34         if(to[i]==fa)continue;
 35         pre(to[i],p);
 36         sz[p]+=sz[to[i]];
 37         if(sz[to[i]]>sz[son[p]])son[p]=to[i];
 38     }
 39 }
 40 
 41 void findtp(int p)
 42 {
 43     if(p==son[f[p]])tp[p]=tp[f[p]];
 44     else tp[p]=p;
 45     for(int i=hd[p];i;i=nx[i])
 46         if(to[i]!=f[p])findtp(to[i]);
 47 }
 48 
 49 int lca(int x,int y)
 50 {
 51     while(tp[x]!=tp[y])
 52         d[tp[x]]>d[tp[y]]?x=f[tp[x]]:y=f[tp[y]];
 53     return d[x]<d[y]?x:y;
 54 }
 55 
 56 int ans[100005];
 57 int lb[400005],rb[400005],mc[400005],mv[400005];
 58 
 59 void build(int num,int l,int r)
 60 {
 61     lb[num]=l,rb[num]=r;
 62     if(l==r)
 63     {
 64         mc[num]=l;
 65         return;
 66     }
 67     int mid=(l+r)>>1;
 68     build(num<<1,l,mid);
 69     build(num<<1|1,mid+1,r);
 70 }
 71 
 72 void update(int num,int p,int v)
 73 {
 74     if(lb[num]==rb[num])
 75     {
 76         mv[num]+=v;
 77         return;
 78     }
 79     int mid=(lb[num]+rb[num])>>1;
 80     if(p<=mid)update(num<<1,p,v);
 81     else update(num<<1|1,p,v);
 82     if(mv[num<<1]>=mv[num<<1|1])
 83         mv[num]=mv[num<<1],mc[num]=mc[num<<1];
 84     else
 85         mv[num]=mv[num<<1|1],mc[num]=mc[num<<1|1];
 86 }
 87 
 88 void add(int p)
 89 {
 90     for(int i=hd[p];i;i=nx[i])
 91         if(to[i]!=f[p])add(to[i]);
 92     for(int i=fir[p];i;i=bes[i])
 93         update(1,kd[i],fl[i]);
 94 }
 95 
 96 void del(int p)
 97 {
 98     for(int i=hd[p];i;i=nx[i])
 99         if(to[i]!=f[p])del(to[i]);
100     for(int i=fir[p];i;i=bes[i])
101         update(1,kd[i],-fl[i]);
102 }
103 
104 void dsu(int p,int keep)
105 {
106     for(int i=hd[p];i;i=nx[i])
107         if(to[i]!=f[p]&&to[i]!=son[p])
108             dsu(to[i],0);
109     if(son[p])dsu(son[p],1);
110     for(int i=hd[p];i;i=nx[i])
111         if(to[i]!=f[p]&&to[i]!=son[p])
112             add(to[i]);
113     for(int i=fir[p];i;i=bes[i])
114         update(1,kd[i],fl[i]);
115     ans[p]=mc[1];
116     if(!keep)del(p);
117 }
118 
119 int main()
120 {
121     scanf("%d%d",&n,&m);
122     for(int i=1;i<n;i++)
123     {
124         int a,b;
125         scanf("%d%d",&a,&b);
126         edge(a,b);
127         edge(b,a);
128     }
129     pre(1,1);
130     findtp(1);
131     build(1,0,100000);
132     for(int i=1;i<=m;i++)
133     {
134         int x,y,z;
135         scanf("%d%d%d",&x,&y,&z);
136         int l=lca(x,y);
137         mark(x,z,1);
138         mark(y,z,1);
139         mark(l,z,-1);
140         if(l!=1)mark(f[l],z,-1);
141     }
142     dsu(1,1);
143     for(int i=1;i<=n;i++)printf("%d\n",ans[i]);
144     return 0;
145 }
洛谷P4556 雨天的尾巴

 

posted @ 2018-09-05 21:04  cervusky  阅读(228)  评论(0编辑  收藏  举报

Contact with me