题目描述
- 深绘里一直很讨厌雨天。
- 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。
- 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉。
- 无奈的深绘里和村民们只好等待救济粮来维生。
- 不过救济粮的发放方式很特别。
- 首先村落里的一共有n 座房屋,并形成一个树状结构。然后救济粮分m 次发放,每次选择两个房屋(x,y),然后对于x 到y 的路径上(含x 和y) 每座房子里发放一袋z 类型的救济粮。
- 然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮。
数据范围
n , m ≤ 1 0 5 n,m \le 10^5 n,m≤105
题目分析
- 都说了如今是 l o g log log的时代!
- 让我说一下两种做法。
树链剖分
- 显然。
- 先差分,对于一个路径 x − > y x->y x−>y,在 x x x与 y y y上+1, L C A LCA LCA上-1, L C A LCA LCA父亲上-1。
- 这样子对于一个点,它救济粮真正个数即为整个子树的和。
- 然后用树链剖分维护一些救济粮的最大值即可。
线段树合并
- 又是一个神奇算法。
- 但要注意线段树合并一般都要与动态开点配套,不然会超时。
- 所谓线段树合并,不要想得那么高深,其实就是直接合并。一个点一个点合并,只不过要是其中有一个数的这个位置“没有点”,直接连边即可。
- 具体方法参见百度优先搜索
- 因为所有点的总和不超过 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n),所以时间复杂度是可以得到保证的。
- 然后对于一个点,把它所有子树的线段树合并,然后把它差分的值加上去,用线段树维护救济粮最大值即可。
代码
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e5+100,maxn=17,Max=1e9;
struct node{
int x,y,next;
}a[2*N];int len,last[N];
struct tree{
int lc,rc,c;
}tr[N*70];int len2=0;
void ins(int x,int y){
a[++len].x=x;a[len].y=y;
a[len].next=last[x];last[x]=len;
}
int fa[N][19],deep[N];bool bk[N];
void dfs(int x){
bk[x]=false;
for(int i=1;i<=maxn;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
for(int k=last[x];k;k=a[k].next){
int y=a[k].y;
if(bk[y]) deep[y]=deep[x]+1,fa[y][0]=x,dfs(y);
}
}
int findlca(int x,int y){
for(int i=maxn;i>=0;i--){
if(deep[fa[x][i]]>=deep[y]) x=fa[x][i];
if(deep[fa[y][i]]>=deep[x]) y=fa[y][i];
}
for(int i=maxn;i>=0;i--){
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
}
if(x!=y) x=fa[x][0],y=fa[y][0];
return x;
}
int Ans[N],root[N];
int mymax(int x,int y) {return x>y?x:y;}
void change(int &now,int L,int R,int k,int c){
if(!now) now=++len2;
if(L==R) {tr[now].c+=c;return ;}
int mid=(L+R)>>1;
int &lc=tr[now].lc,&rc=tr[now].rc;
if(k<=mid) change(lc,L,mid,k,c);
else change(rc,mid+1,R,k,c);
tr[now].c=mymax(tr[lc].c,tr[rc].c);
}
void merge(int &nowx,int &nowy,int L,int R){
if(!nowy) return ;
if(!nowx) {nowx=nowy;return ;}
if(L==R) {tr[nowx].c+=tr[nowy].c;return ;}
int mid=(L+R)>>1;
merge(tr[nowx].lc,tr[nowy].lc,L,mid);
merge(tr[nowx].rc,tr[nowy].rc,mid+1,R);
int lc=tr[nowx].lc,rc=tr[nowx].rc;
tr[nowx].c=mymax(tr[lc].c,tr[rc].c);
}
int findans(int now,int L,int R){
if(L==R) return L;
int mid=(L+R)>>1;
int lc=tr[now].lc,rc=tr[now].rc;
if(!lc) return findans(rc,mid+1,R);
if(!rc) return findans(lc,L,mid);
if(tr[lc].c>=tr[rc].c) return findans(lc,L,mid);
else return findans(rc,mid+1,R);
}
void dfs2(int x){
for(int k=last[x];k;k=a[k].next){
int y=a[k].y;
if(fa[y][0]==x){
dfs2(y);
merge(root[x],root[y],1,Max);
}
}
if(tr[root[x]].c<=0) Ans[x]=0;
else Ans[x]=findans(root[x],1,Max);
}
int main()
{
int n,m;scanf("%d%d",&n,&m);
len=0;memset(last,0,sizeof(last));
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}
deep[1]=1;memset(bk,true,sizeof(bk));dfs(1);
for(int i=1;i<=n;i++) root[i]=i,len2++;
for(int i=1;i<=m;i++){
int x,y,z;scanf("%d%d%d",&x,&y,&z);
int lca=findlca(x,y);
change(root[x],1,Max,z,1);
change(root[y],1,Max,z,1);
change(root[lca],1,Max,z,-1);
if(fa[lca][0]) change(root[fa[lca][0]],1,Max,z,-1);
}
dfs2(1);
for(int i=1;i<=n;i++) printf("%d\n",Ans[i]);
return 0;
}