3397. 【GDOI2014模拟】雨天的尾巴

题目描述
  • 深绘里一直很讨厌雨天。
  • 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。
  • 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉。
  • 无奈的深绘里和村民们只好等待救济粮来维生。
  • 不过救济粮的发放方式很特别。
  • 首先村落里的一共有n 座房屋,并形成一个树状结构。然后救济粮分m 次发放,每次选择两个房屋(x,y),然后对于x 到y 的路径上(含x 和y) 每座房子里发放一袋z 类型的救济粮。
  • 然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮。
数据范围

n , m ≤ 1 0 5 n,m \le 10^5 n,m105

题目分析
  • 都说了如今是 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值