【BZOJ4009】【HNOI2015】—接水果(整体二分+扫描线)

传送门

描述
风见幽香非常喜欢玩一个叫做 osu! 的游戏,其中她最喜欢玩的模式就是接水果。由于她已经 DT FC 了 The big black, 她觉得这个游戏太简单了,于是发明了一个更加难的版本。
首先有一个地图,是一棵由 n 个顶点、n−1 条边组成的树(例如图 111 给出的树包含 888 个顶点、777 条边)。这颗树上有 P 个盘子,每个盘子实际上是一条路径(例如图 1 中顶点 6 到顶点 8 的路径),并且每个盘子还有一个权值。第 i 个盘子就是顶点 ai 到顶点 bi 的路径(由于是树,所以从 ai 到 bi 的路径是唯一的),权值为 ci 。接下来依次会有 Q 个水果掉下来,每个水果本质上也是一条路径,第 i个水果是从顶点 ui 到顶点 vi 的路径。
幽香每次需要选择一个盘子去接当前的水果:一个盘子能接住一个水果,当且仅当盘子的路径是水果的路径的子路径(例如图 1中从 3 到 7 的路径是从 1 到 8 的路径的子路径)。这里规定:从 a 到 b 的路径与从 b 到 a 的路径是同一条路径。当然为了提高难度,对于第 i 个水果,你需要选择能接住它的所有盘子中,权值第 ki 小的那个盘子,每个盘子可重复使用(没有使用次数的上限:一个盘子接完一个水果后,后面还可继续接其他水果,只要它是水果路径的子路径)。幽香认为这个游戏很难,你能轻松解决给她看吗?
在这里插入图片描述
输入
第一行三个数 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 小一定存在。
输出
对于每个果子,输出一行表示选择的盘子的权值。
样例输入
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
样例输出
442139372
333042360
442139372
283254485
283254485
217394434
217394434
217394434
217394434
217394434
提示
对于所有数据,N,P,Q≤40000

感觉也不是很难吧

显然可以发现题目就是在求所有被某条路径覆盖的路径的第 k k
显然 可以发现对于当前路径 ( u , v ) (u,v) ,把它覆盖的路径 ( x , y ) (x,y) 只有2种情况

1: l c a ( u , v ) = u lca(u,v)=u

那么只有满足 x S u b t r e e u , y S u b t r e e v x\notin Subtree_u,y\in Subtree_v
转化为 d f s dfs 序即
d f n [ x ] [ 1 , d f n [ u ] ] [ d f n [ u ] + s i z [ u ] 1 , n ] dfn[x]\in [1,{dfn[u]]\bigcup [dfn[u]+siz[u]-1},n]
d f n [ y ] [ d f n [ v ] , d f n [ v ] + s i z [ v ] 1 ] dfn[y]\in [dfn[v],dfn[v]+siz[v]-1]

如果把 d f n [ u ] , d f n [ v ] dfn[u],dfn[v] 分别看成一个二维的点,那就是 ( d f n [ x ] , d f n [ y ] ) (dfn[x],dfn[y]) 处在2个矩形中

2: l c d ( u , v ) ̸ = u lcd(u,v)\not=u

那也就是 x S u b t r e e u , y S u b t r e e v x\in Subtree_u,y\in Subtree_v

d f n [ x ] [ d f n [ u ] , d f n [ u ] + s i z [ u ] 1 ] dfn[x]\in [dfn[u],dfn[u]+siz[u]-1]
d f n [ y ] [ d f n [ v ] , d f n [ v ] + s i z [ v ] 1 ] dfn[y]\in [dfn[v],dfn[v]+siz[v]-1]
也就是被包含在一个矩形里

那现在问题就变成了对一个点求包含它的矩形中的第 k k
那就可以整体二分
就变成了一个二维数点问题了
扫描线+ B i t Bit 就可以了

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	char ch=getchar();
	int res=0,f=1;
	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
	while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
	return res*f;
}
const int N=50006;
struct plate{
	int x1,x2,l1,r1,l2,r2,k;
	friend inline bool operator <(const plate &a,const plate &b){
		return a.k<b.k;
	}
}p[N];
struct line{
	int x,l,r,t;
	friend inline bool operator <(const line &a,const line &b){
		return a.x<b.x;
	}
}p1[N<<1];
struct ask{
	int x,y,k,id;
	friend inline bool operator <(const ask &a,const ask &b){
		return a.x<b.x;
	}
}q[N],tmp[N];
struct lineask{
	int x,y,p;
	friend inline bool operator <(const lineask &a,const lineask &b){
		return a.x<b.x;
	}
}q1[N<<1];
int adj[N],nxt[N<<1],to[N<<1],dep[N],siz[N],son[N],top[N],in[N],out[N],fa[N],dfn,cnt,tot;
int ans[N],tr[N],n,m,qn,now[N];
inline void addedge(int u,int v){
	nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v;
}
inline int lowbit(int x){
	return x&(-x);
}
inline void update(int p,int k){
	for(;p<=n;p+=lowbit(p))tr[p]+=k;
}
inline int query(int p,int res=0){
	for(;p;p-=lowbit(p))res+=tr[p];return res;
}
inline int Lson(int u,int v){
	int t;
	while(top[u]!=top[v])t=top[v],v=fa[top[v]];
	return u==v?t:son[u];
}
void dfs1(int u){
	siz[u]=1;
	for(int e=adj[u];e;e=nxt[e]){
		int v=to[e];
		if(v==fa[u])continue;
		dep[v]=dep[u]+1,fa[v]=u;
		dfs1(v),siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
	}
}
void dfs2(int u,int tp){
	in[u]=++tot,top[u]=tp;
	if(son[u])dfs2(son[u],tp);
	for(int e=adj[u];e;e=nxt[e]){
		int v=to[e];
		if(v==fa[u]||v==son[u])continue;
		dfs2(v,v);
	}
	out[u]=tot;
}
void solve(int l,int r,int st,int des){
	if(st>des)return;
	if(l==r){
		for(int i=st;i<=des;i++)ans[q[i].id]=p[l].k;return;
	}
	int mid=(l+r)>>1,cnt1=0,cnt2=0;
	for(int i=l;i<=mid;i++){
		if(p[i].l1<=p[i].r1){
			p1[++cnt1]=(line){p[i].x1,p[i].l1,p[i].r1,1};
			p1[++cnt1]=(line){p[i].x2+1,p[i].l1,p[i].r1,-1};
		}
		if(p[i].l2<=p[i].r2){
			p1[++cnt1]=(line){p[i].x1,p[i].l2,p[i].r2,1};
			p1[++cnt1]=(line){p[i].x2+1,p[i].l2,p[i].r2,-1};
		}
	}
	for(int i=st;i<=des;i++){
		q1[++cnt2]=(lineask){q[i].x,q[i].y,i};
		q1[++cnt2]=(lineask){q[i].y,q[i].x,i};
		now[i]=0;
	}
	sort(p1+1,p1+cnt1+1),sort(q1+1,q1+cnt2+1);
	int j=1;
	for(int i=1;i<=cnt2;i++){
		while(j<=cnt1&&p1[j].x<=q1[i].x)update(p1[j].l,p1[j].t),update(p1[j].r+1,-p1[j].t),j++;
		now[q1[i].p]+=query(q1[i].y);
	}
	int L=st,R=des;
	for(int i=st;i<=des;i++)if(now[i]>=q[i].k)tmp[L++]=q[i];else q[i].k-=now[i],tmp[R--]=q[i];
	for(int i=st;i<=des;i++)q[i]=tmp[i];
	for(int i=1;i<j;i++)update(p1[i].l,-p1[i].t),update(p1[i].r+1,p1[i].t);
	solve(l,mid,st,R),solve(mid+1,r,R+1,des);
}
int main(){
	n=read(),m=read(),qn=read();
	for(int i=1;i<n;i++){
		int u=read(),v=read();
		addedge(u,v),addedge(v,u);
	}
	dfs1(1),dfs2(1,1);
	for(int i=1;i<=m;i++){
		int u=read(),v=read(),w=read();
		if(dep[u]<dep[v])swap(u,v);
		if(in[v]<=in[u]&&out[u]<=out[v])v=Lson(v,u),p[i]=(plate){in[u],out[u],1,in[v]-1,out[v]+1,n,w};
		else p[i]=(plate){in[u],out[u],in[v],out[v],1,0,w};
	}
	sort(p+1,p+m+1);
	for(int i=1;i<=qn;i++){
		q[i].id=i,q[i].x=in[read()],q[i].y=in[read()],q[i].k=read();
	}
	solve(1,m,1,qn);
	for(int i=1;i<=qn;i++)cout<<ans[i]<<'\n';
}

猜你喜欢

转载自blog.csdn.net/qq_42555009/article/details/88134432
今日推荐