[HNOI2015]接水果

Description
风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果。由于她已经DT FC 了The big black, 她觉得这个游戏太简单了,于是发明了一个更加难的版本。首先有一个地图,是一棵由 n 个顶点、n-1 条边组成的树(例如图 1给出的树包含 8 个顶点、7 条边)。这颗树上有 P 个盘子,每个盘子实际上是一条路径(例如图 1 中顶点 6 到顶点 8 的路径),并且每个盘子还有一个权值。第 i 个盘子就是顶点a_i到顶点b_i的路径(由于是树,所以从a_i到b_i的路径是唯一的),权值为c_i。接下来依次会有Q个水果掉下来,每个水果本质上也是一条路径,第i 个水果是从顶点 u_i 到顶点v_i 的路径。幽香每次需要选择一个盘子去接当前的水果:一个盘子能接住一个水果,当且仅当盘子的路径是水果的路径的子路径(例如图1中从 3到7 的路径是从1到8的路径的子路径)。这里规定:从a 到b的路径与从b到 a的路径是同一条路径。当然为了提高难度,对于第 i 个水果,你需要选择能接住它的所有盘子中,权值第 k_i 小的那个盘子,每个盘子可重复使用(没有使用次数的上限:一个盘子接完一个水果后,后面还可继续接其他水果,只要它是水果路径的子路径)。幽香认为这个游戏很难,你能轻松解决给她看吗?

Input
第一行三个数 n和P 和Q,表示树的大小和盘子的个数和水果的个数。

接下来n-1 行,每行两个数 a、b,表示树上的a和b 之间有一条边。树中顶点
按1到 n标号。 接下来 P 行,每行三个数 a、b、c,表示路径为 a 到 b、权值为 c 的盘子,其
中0≤c≤10^9,a不等于b。
接下来Q行,每行三个数 u、v、k,表示路径为 u到 v的水果,其中 u不等于v,你需要选择第 k小的盘子,
第k 小一定存在。

Output
对于每个果子,输出一行表示选择的盘子的权值。

Sample Input
10 10 10
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
3 2 217394434
10 7 13022269
6 7 283254485
6 8 333042360
4 6 442139372
8 3 225045590
10 4 922205209
10 8 808296330
9 2 486331361
4 9 551176338
1 8 5
3 8 3
3 8 4
1 8 3
4 8 1
2 3 1
2 3 1
2 3 1
2 4 1
1 4 1

Sample Output
442139372
333042360
442139372
283254485
283254485
217394434
217394434
217394434
217394434
217394434

HINT
N,P,Q<=40000。


考虑一下题型的转化

我们可以把每个水果路径端点的dfs序提出来,将其变成一个平面内的点

然后我们考虑一个盘子(u,v)能够接到哪些水果(l,r)(我们这里令dfn[u]<dfn[v],dfn[l]<dfn[r])

如果lca为u,那么这个盘子能接到的水果的l,r需要满足

\(\begin{cases}1&\leqslant l'<x'\\v'&\leqslant r'<v'+size[v]\end{cases}\)\(\begin{cases}x'+size[x]&<l'\leqslant n\\v'&\leqslant r'<v'+size[v]\end{cases}\)

其中,l',r'等表示dfn[l],dfn[r]...,size[v]表示以v为根的子树大小,x为v到u路径上,u的儿子节点

如果lca不为u,那么这个盘子能接到的水果的l,r需要满足

\[\begin{cases}u'\leqslant l'<u'+size[u]\\v'\leqslant r'<v'+size[v]\end{cases} \]

这样的话我们可以发现,每个盘子都可以表示成平面内的一个矩阵,如果有某个点在其覆盖范围内,就说明这个点,也就是这个点表示的水果能被这个矩阵表示的盘子接到

于是问题转化为了给定平面内一些点和一些带权值的矩阵,对于每个点求出覆盖其的矩阵权值的第k小

这样的话我们可以使用整体二分,当然也可以使用树套树,这里我采用后面一种写法

我们将平面中x轴排序,使用扫描线,如果使用Lazy标记的话,空间肯定开不下,因此我们采用标记永久化。

我们使用权值线段树套位置线段树,反过来的话求第\(k\)大较为麻烦

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define Fi first
#define Se second
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline char gc(){
	static char buf[1000000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int frd(){
	int x=0,f=1;char ch=gc();
	for (;ch<'0'||ch>'9';ch=gc())	if (ch=='-')    f=-1;
	for (;ch>='0'&&ch<='9';ch=gc())	x=(x<<1)+(x<<3)+ch-'0';
	return x*f;
}
inline int read(){
	int x=0,f=1;char ch=getchar();
	for (;ch<'0'||ch>'9';ch=getchar())	if (ch=='-')	f=-1;
	for (;ch>='0'&&ch<='9';ch=getchar())	x=(x<<1)+(x<<3)+ch-'0';
	return x*f;
}
inline void print(int x){
	if (x<0)    putchar('-'),x=-x;
	if (x>9)	print(x/10);
	putchar(x%10+'0');
}
const int N=4e4,M=2e7;
int list[N+10],dfn[N+10],Ans[N+10];
int n,P,Q;
struct S1{
	int pre[(N<<1)+10],now[N+10],child[(N<<1)+10],tot,Time;
	int fa[N+10],deep[N+10],top[N+10],Rem[N+10],size[N+10];
	void join(int x,int y){pre[++tot]=now[x],now[x]=tot,child[tot]=y;}
	void insert(int x,int y){join(x,y),join(y,x);}
	void dfs(int x){
		deep[x]=deep[fa[x]]+1,size[x]=1;
		for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
			if (son==fa[x])	continue;
			fa[son]=x,dfs(son);
			size[x]+=size[son];
			if (size[Rem[x]]<size[son])	Rem[x]=son;
		}
	}
	void build(int x){
		if (!x)	return;
		dfn[x]=++Time;
		top[x]=Rem[fa[x]]==x?top[fa[x]]:x;
		build(Rem[x]);
		for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
			if (son==fa[x]||son==Rem[x])	continue;
			build(son);
		}
	}
	pair<int,int> Lca(int x,int y){
		int Last=0;
		while (top[x]!=top[y]){
			if (deep[top[x]]<deep[top[y]])	swap(x,y);
			Last=top[x],x=fa[top[x]];
		}
		if (x==y)	return make_pair(x,Last);
		if (deep[x]>deep[y])	swap(x,y);
		return make_pair(x,Rem[x]);
	}
}HLD;//Heavy Light Decomposition
struct S2{
	int root[(N<<2)+10];
	struct S3{
		int ls[M+10],rs[M+10],cnt[M+10],tot;
		void Modify(int &p,int l,int r,int x,int y,int type){
			if (!p)	p=++tot;
			if (x<=l&&r<=y){
				cnt[p]+=type;
				return;
			}
			int mid=(l+r)>>1;
			if (x<=mid)	Modify(ls[p],l,mid,x,y,type);
			if (y>mid)	Modify(rs[p],mid+1,r,x,y,type);
		}
		int Query(int p,int l,int r,int x){
			if (l==r)	return cnt[p];
			int mid=(l+r)>>1;
			if (x<=mid)	return Query(ls[p],l,mid,x)+cnt[p];
			else	return Query(rs[p],mid+1,r,x)+cnt[p];
		}
	}PST;//Position Segment Tree
	#define ls (p<<1)
	#define rs (p<<1|1)
	void Modify(int p,int l,int r,int v,int x,int y,int type){
		PST.Modify(root[p],1,n,x,y,type);
		if (l==r)	return;
		int mid=(l+r)>>1;
		if (v<=mid)	Modify(ls,l,mid,v,x,y,type);
		else	Modify(rs,mid+1,r,v,x,y,type);
	}
	int Query(int p,int l,int r,int k,int x){
		if (l==r)	return list[l];
		int mid=(l+r)>>1,res=PST.Query(root[ls],1,n,x);
		if (k<=res)	return Query(ls,l,mid,k,x);
		else	return Query(rs,mid+1,r,k-res,x);
	}
	#undef ls
	#undef rs
}PST_VST;//Position Segment Tree in Value Segment Tree
struct S4{
	int x,l,r,k,type;
	void insert(int _x,int _l,int _r,int _k,int _type){x=_x,l=_l,r=_r,k=_k,type=_type;}
	bool operator <(const S4 &tis)const{return x<tis.x;}
}plate[(N<<3)+10];
struct S5{
	int x,y,k,ID;
	void insert(int _x,int _y,int _k,int _ID){x=_x,y=_y,k=_k,ID=_ID;}
	bool operator <(const S5 &tis)const{return x<tis.x;}
}fruit[N+10];
int cnt;
void insert(int L,int R,int l,int r,int k){
	plate[++cnt].insert(L,l,r,k,1);
	plate[++cnt].insert(R+1,l,r,k,-1);
}
void Ins(int L,int R,int l,int r,int k){
	if (L>R)	return;
	insert(L,R,l,r,k);
	insert(l,r,L,R,k);
}
int main(){
	n=read(),P=read(),Q=read();
	for (int i=1;i<n;i++){
		int x=read(),y=read();
		HLD.insert(x,y);
	}
	HLD.dfs(1),HLD.build(1);
	for (int i=1;i<=P;i++){
		int x=read(),y=read(),k=read();
		pair<int,int>tmp=HLD.Lca(x,y);
		if (dfn[x]>dfn[y])	swap(x,y);
		if (tmp.Fi==x){
			Ins(1,dfn[tmp.Se]-1,dfn[y],dfn[y]+HLD.size[y]-1,k);
			Ins(dfn[tmp.Se]+HLD.size[tmp.Se],n,dfn[y],dfn[y]+HLD.size[y]-1,k);
		}else	Ins(dfn[x],dfn[x]+HLD.size[x]-1,dfn[y],dfn[y]+HLD.size[y]-1,k);
		list[i]=k;
	}
	sort(list+1,list+1+P);
	int T=unique(list+1,list+1+P)-list-1;
	for (int i=1;i<=cnt;i++)	plate[i].k=lower_bound(list+1,list+1+T,plate[i].k)-list;
	for (int i=1;i<=Q;i++){
		int x=read(),y=read(),k=read();
		if (dfn[x]>dfn[y])	swap(x,y);
		fruit[i].insert(dfn[x],dfn[y],k,i);
	}
	sort(fruit+1,fruit+1+Q);
	sort(plate+1,plate+1+cnt);
	for (int i=1,j=1;i<=Q;i++){
		while (j<=cnt&&plate[j].x<=fruit[i].x)	PST_VST.Modify(1,1,n,plate[j].k,plate[j].l,plate[j].r,plate[j].type),j++;
		Ans[fruit[i].ID]=PST_VST.Query(1,1,n,fruit[i].k,fruit[i].y);
	}
	for (int i=1;i<=Q;i++)	printf("%d\n",Ans[i]);
	return 0;
}
posted @ 2018-11-22 12:00  Wolfycz  阅读(318)  评论(0编辑  收藏  举报