这道题可以用线段树合并做,网上的题解基本上都是线段树合并的。
但是为什么我就偏偏要用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用的树剖。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 }