【GDOI2014模拟】雨天的尾巴

题目

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

分析

相信大多数人都跟我一样,看到这道题,果断认为是树链剖分。
其实不然(我1.5h把熟练剖分打出来,以为能过50%。结果打错了,20%。然后。。。打暴力的人都是50%,#%……&*)。
我们注意到z<=10^9,那么的数据范围。
但是m<=100000,所以就先做个离散化。
。。。
接着用到个很神奇的东西,叫线段树合并。
对于树上的每一个节点开一棵线段树,记录该节点的每个z的数量以及某一段z的最大值。注意要动态开节点,否则会爆空间。
发现,如果要修改从
xy的路径,其实就是将x的z值加一,y的z值加一,lca(x,y)的z值减一以及fa[lca(x,y)]的z值减一(why?因为当线段树合并后从xlca(x,y)的路径上和从ylca(x,y)的路径上的每个节点的z值都加一,但lca(x,y)这个节点重复加了,那么就减掉。不过剩余的z值还会继续上传,所以在fa[lca(x,y)]**就把上传的z值减去)
合并操作

int mesh(int x,int y,int l,int r)
{
	if(l==r)
	{
		tree[x].v+=tree[y].v;
		return 0;                                                    
	}
	int mid=(l+r)/2;
	if(tree[y].l)
	{
		if(!tree[x].l)
			tree[x].l=tree[y].l;
			else
		mesh(tree[x].l,tree[y].l,l,mid);
	}
	if(tree[y].r)
	{
		if(!tree[x].r)
			tree[x].r=tree[y].r;
			else
		mesh(tree[x].r,tree[y].r,mid+1,r);
	}
	tree[x].v=max(tree[tree[x].l].v,tree[tree[x].r].v);
}

当然,当所有的修改操作做完后才线段树合并,否则会超时。


#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
const int maxlongint=2147483647;
using namespace std;
struct trees
{
	int l,r,v;
}tree[8000000];
struct read
{
	int x,y,z;
}re[110000];
int next[201000],last[201000],to[201000],po,tot,n,m,g[200000][20],t[200000],deep[200000],f[200000],sum,ans[2000000];
bool cmp(read x,read y)
{
	return x.z<y.z;
}
int bj(int x,int y)
{
	next[++tot]=last[x];
	last[x]=tot;
	to[tot]=y;
}
int dg(int x)
{
	for(int i=last[x];i;i=next[i])
	{
		int j=to[i];
		if(j!=g[x][0])
		{
			deep[j]=deep[x]+1;
			g[j][0]=x;
			dg(j);
		}
	}
}
int prelca()
{
	for(int j=1;j<=log2(n);j++)
	{
		for(int i=1;i<=n;i++)
		{
			g[i][j]=g[g[i][j-1]][j-1];
		}
	}
}
int lca(int x,int y)
{
	if(deep[x]>deep[y])
	{
		x=x^y;
		y=x^y;
		x=x^y;
	}
	for(int i=log2(n);i>=0;i--)
	{
		if(deep[g[y][i]]>deep[x])
			y=g[y][i];
	}
	if(deep[y]!=deep[x]) y=g[y][0];
	for(int i=log2(n);i>=0;i--)
	{
		if(g[y][i]!=g[x][i])
		{
			y=g[y][i];
			x=g[x][i];
		}
	}
	if(x!=y) y=g[y][0];
	return y;
}
int put(int v,int l,int r,int x,int y)
{
	if(l==r)
	{
		tree[v].v+=y;
		return 0;
	}
	int mid=(l+r)/2;
	if(x<=mid)
	{
		if(!tree[v].l)
			tree[v].l=++sum;
		put(tree[v].l,l,mid,x,y);
	}
	else
	{
		if(!tree[v].r)
			tree[v].r=++sum;
		put(tree[v].r,mid+1,r,x,y);	
	}
	tree[v].v=max(tree[tree[v].l].v,tree[tree[v].r].v);
}
int work(int x,int y,int z)
{
	int lc=lca(x,y);
	if(!f[x])
	{
		f[x]=++sum;
	}
	put(f[x],1,tot,z,1);
	if(!f[y])
	{
		f[y]=++sum;
	}
	put(f[y],1,tot,z,1);
	if(!f[lc])
	{
		f[lc]=++sum;
	}
	put(f[lc],1,tot,z,-1);
	if(g[lc][0])
	{
		if(!f[g[lc][0]]) f[g[lc][0]]=++sum;
		put(f[g[lc][0]],1,tot,z,-1);
	}
}
int mesh(int x,int y,int l,int r)
{
	if(l==r)
	{
		tree[x].v+=tree[y].v;
		return 0;                                                    
	}
	int mid=(l+r)/2;
	if(tree[y].l)
	{
		if(!tree[x].l)
			tree[x].l=tree[y].l;
			else
		mesh(tree[x].l,tree[y].l,l,mid);
	}
	if(tree[y].r)
	{
		if(!tree[x].r)
			tree[x].r=tree[y].r;
			else
		mesh(tree[x].r,tree[y].r,mid+1,r);
	}
	tree[x].v=max(tree[tree[x].l].v,tree[tree[x].r].v);
}
int find(int v,int l,int r)
{
	if(l==r)
	{
		return l;
	}
	if(tree[tree[v].l].v==0 && tree[tree[v].r].v==0)
		return 0;
	int mid=(l+r)/2;
	if(tree[tree[v].l].v>=tree[tree[v].r].v)
	{
		return find(tree[v].l,l,mid);
	}
	else
	{
		return find(tree[v].r,mid+1,r);	
	}
}
int merge(int x)
{
	for(int i=last[x];i;i=next[i])
	{
		int j=to[i];
		if(j!=g[x][0])
		{
			merge(j);
			if(!f[x])
			{		
				f[x]=++sum;
			}	
			if(!f[j])
			{		
				f[j]=++sum;
			}
			mesh(f[x],f[j],1,tot);
		}
	}
	ans[x]=find(f[x],1,tot);
}
int main()
{
	scanf("%d",&n);
	scanf("%d",&m);
	for(int i=1;i<=n-1;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		bj(x,y);
		bj(y,x);
	}
	deep[1]=1;
	dg(1);
	prelca();
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&re[i].x,&re[i].y,&re[i].z);
	}
	sort(re+1,re+m+1,cmp);
	tot=0;
	sum=0;
	for(int i=1;i<=m;i++)
	{
		if(re[i].z==t[tot])
		{
			re[i].z=tot;
		}
		else
		{
			t[++tot]=re[i].z;
			re[i].z=tot;
		}
	}
	for(int i=1;i<=m;i++)
	{
		work(re[i].x,re[i].y,re[i].z);
	}
	merge(1);
	for(int i=1;i<=n;i++)
	{
		if(!f[i])
		{		
			f[i]=++sum;
		}	
		printf("%d
",t[ans[i]]);
	}
}


原文地址:https://www.cnblogs.com/chen1352/p/9026702.html