2018.10.02 bzoj4009: [HNOI2015]接水果(整体二分)

传送门
整体二分好题。


考虑水果被盘子接住的条件。
不妨设水果表示的路径为 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),盘子表示的为 ( x 2 , y 2 ) (x_2,y_2) (x2,y2)
不妨设 d f n [ x 2 ] > d f n [ y 2 ] , d f n [ x 1 ] > d f n [ y 1 ] dfn[x_2]>dfn[y_2],dfn[x_1]>dfn[y_1] dfn[x2]>dfn[y2],dfn[x1]>dfn[y1]
那么有两种情况。
第一种:
y 2 y_2 y2 x 2 x_2 x2的祖先。
这时水果需要覆盖掉这条链。
那么 x 1 x_1 x1 x 2 x_2 x2子树中, y 1 y_1 y1 y 2 y_2 y2子树外。
第二种:
y 2 y_2 y2不是 x 2 x_2 x2的祖先。
那么, x 1 x_1 x1 x 2 x_2 x2的子树中, y 1 y_1 y1 y 2 y_2 y2的子树中。
发现这两种情况如果用dfs序表示就对应着点在矩形中的关系。
于是我们用整体二分求出覆盖每个点的第k小矩形的下标。
代码:

#include<bits/stdc++.h>
#define N 40005
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int n,p,q,first[N],cnt=0,in[N],out[N],dep[N],st[N][21],pred[N],bit[N],ans[N],tot=0,cnt1=0,cnt2=0;
struct edge{int v,next;}e[N<<1];
struct Q{int u,v,k,id;}qry[N],qtmp[N];
struct Upd{int typ,x,y1,y2,tmp;}upd[N<<2];
inline void add(int u,int v){e[++cnt].v=v,e[cnt].next=first[u],first[u]=cnt;}
inline void dfs(int p){
	pred[in[p]=++tot]=p;
	for(int i=1;i<=20;++i)st[p][i]=st[st[p][i-1]][i-1];
	for(int i=first[p];i;i=e[i].next){
		int v=e[i].v;
		if(v==st[p][0])continue;
		dep[v]=dep[p]+1,st[v][0]=p,dfs(v);
	}
	out[p]=tot;
}
inline int lca(int u,int v){
	if(dep[u]<dep[v])swap(u,v);
	int tmp=dep[u]-dep[v];
	for(int i=20;~i;--i)if((tmp>>i)&1)u=st[u][i];
	if(u==v)return u;
	for(int i=20;~i;--i)if(st[u][i]!=st[v][i])u=st[u][i],v=st[v][i];
	return st[u][0];
}
struct Plate{int x1,x2,y1,y2,v;}plate[N*2];
inline void update(int x,int v){for(int i=x;i<=n;i+=i&-i)bit[i]+=v;}
inline int query(int x){int ret=0;for(int i=x;i;i-=i&-i)ret+=bit[i];return ret;}
inline bool cmp(Plate a,Plate b){return a.v<b.v;}
inline bool cmp1(Upd a,Upd b){return a.x==b.x?a.typ<b.typ:a.x<b.x;}
inline void solve(int l,int r,int ql,int qr){
	if(l>r)return;
	if(ql==qr){
		for(int i=l;i<=r;++i)ans[qry[i].id]=plate[ql].v;
		return;
	}
	int mid=ql+qr>>1,siz=0;
	for(int i=ql;i<=mid;++i){
		upd[++siz]=(Upd){0,plate[i].x1,plate[i].y1,plate[i].y2,1};
		upd[++siz]=(Upd){2,plate[i].x2,plate[i].y1,plate[i].y2,-1};
	}
	for(int i=l;i<=r;++i)upd[++siz]=(Upd){1,qry[i].u,qry[i].v,i,0};
	sort(upd+1,upd+siz+1,cmp1);
	int hd=l-1,tl=r+1;
	for(int i=1;i<=siz;++i){
		if(upd[i].typ==1){
			int val=query(upd[i].y1),tmp=upd[i].y2;
			if(qry[tmp].k<=val)qtmp[++hd]=qry[tmp];
			else qry[tmp].k-=val,qtmp[--tl]=qry[tmp];
		}
		else update(upd[i].y1,upd[i].tmp),update(upd[i].y2+1,-upd[i].tmp);
	}
	for(int i=l;i<=r;++i)qry[i]=qtmp[i];
	solve(l,hd,ql,mid),solve(tl,r,mid+1,qr);
}
int main(){
	n=read(),p=read(),q=read();
	for(int i=1;i<n;++i){
		int u=read(),v=read();
		add(u,v),add(v,u);
	}
	dfs(1);
	for(int i=1;i<=p;++i){
		int u=read(),v=read(),w=read();
		if(in[u]>in[v])swap(u,v);
		int t=lca(u,v);
		if(t!=u)plate[++cnt1]=(Plate){in[u],out[u],in[v],out[v],w};
		else{
			int tmp=dep[v]-dep[u]-1,pre=v;
			for(int i=20;~i;--i)if((tmp>>i)&1)pre=st[pre][i];
			plate[++cnt1]=(Plate){1,in[pre]-1,in[v],out[v],w};
			if(out[v]<n)plate[++cnt1]=(Plate){in[v],out[v],out[pre]+1,n,w};
		}
	}
	sort(plate+1,plate+cnt1+1,cmp);
	for(int i=1;i<=q;++i){
		int u=read(),v=read(),k=read();
		if(in[u]>in[v])swap(u,v);
		qry[i]=(Q){in[u],in[v],k,i};
	}
	solve(1,q,1,cnt1);
	for(int i=1;i<=q;++i)printf("%d\n",ans[i]);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值