P3242 [HNOI2015] 接水果(整体二分、扫描线、dfs序)

解析

一道有点毒瘤的题
也是一道感觉真的可以出现在考场上的很综合的题
做的还可以
除了一开始把盘子和水果看反白写了各树套树之外
为什么盘子是水果的子路径啊

由于是做专题爬过来的多次询问区间第k小,想到整体二分
那么重点就是子路径的判定问题
发现,对于一个盘子 ( u , v ) (u,v) (u,v)
分两种情况:

  1. 一般路径,那么可以被接到的水果 ( x , y ) (x,y) (x,y) 的x和y必须分别在u和v的子树中,换句话说就是 d f n u ≤ d f n x ≤ d f n u + s i z u − 1 & d f n v ≤ d f n y ≤ d f n v + s i z v − 1 dfn_u\le dfn_x\le dfn_u+siz_u-1 \& dfn_v\le dfn_y\le dfn_v+siz_v-1 dfnudfnxdfnu+sizu1&dfnvdfnydfnv+sizv1(对称同理)
  2. 返祖链,假设u是祖先,son是 ( u , v ) (u,v) (u,v) 上u下方的第一个儿子,此时接到的水果 ( x , y ) (x,y) (x,y) 必须满足x在son的子树外,y在v的子树内,也就是 ( d f n u ≤ d f n x < d f n s o n ∣ ∣ d f n u > d f n s o n + s i z s o n − 1 ) & d f n v ≤ d f n y ≤ d f n v + s i z v − 1 (dfn_u\le dfn_x< dfn_{son}||dfn_u>dfn_{son}+siz_{son}-1) \& dfn_v\le dfn_y\le dfn_v+siz_v-1 (dfnudfnx<dfnsondfnu>dfnson+sizson1)&dfnvdfnydfnv+sizv1

那么我们把路径 ( u , v ) (u,v) (u,v) 抽象成二维平面上的一个点 ( d f n u , d f n v ) (dfn_u,dfn_v) (dfnu,dfnv) ,问题就变成了矩形加单点求和问题
由于本题是静态的,所以可以直接扫描线一个log解决
加上整体二分的log,总复杂度 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n)
注意数组不要开小!!

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=4e5+100;
const int mod=1e9+7;
inline ll read(){
	ll x(0),f(1);char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int n,m,Q;

struct node{
	int to,nxt;
}p[N<<1];
int fi[N],cnt;
inline void addline(int x,int y){
	p[++cnt]=(node){y,fi[x]};fi[x]=cnt;
	return;
}
int pl[N][19],pos[N],siz[N],tim;
void dfs(int x,int f){
	pl[x][0]=f;
	for(int k=1;pl[x][k-1];k++) pl[x][k]=pl[pl[x][k-1]][k-1];
	pos[x]=++tim;siz[x]=1;
	for(int i=fi[x];~i;i=p[i].nxt){
		int to=p[i].to;
		if(to==f) continue;
		dfs(to,x);
		siz[x]+=siz[to];
	}
	return;
}
inline int Lca(int x,int y,int op){
	if(pos[x]<=pos[y]&&pos[y]<=pos[x]+siz[x]-1){
		assert(op);
		return x;
	}
	for(int k=16;k>=0;k--){
		int o=pl[x][k];
		if(!o||(pos[o]<=pos[y]&&pos[y]<=pos[o]+siz[o]-1)) continue;
		x=o;
	}
	return op?pl[x][0]:x;
}

struct ope{
	int op;
	int x,y,k,id;//op=2
	int h,l,r,w,f;//op=1
}q[N],q1[N],q2[N];
bool cmp1(ope a,ope b){return a.h<b.h;}
bool cmp2(ope a,ope b){return a.x<b.x;}
int w[N],f[N],tot;
inline void add(int p,int w){
	for(;p<=n;p+=p&-p) f[p]+=w;
	return;
}
inline int ask(int p){
	int res(0);
	for(;p;p-=p&-p) res+=f[p];
	return res;
}
int ans[N];
void print(ope o){
	if(o.op==1){
		printf("  change: h=%d (%d %d) w=%d op=%d\n",o.h,o.l,o.r,o.w,o.f);
	}
	else{
		printf("  ask: x=%d y=%d k=%d id=%d\n",o.x,o.y,o.k,o.id);
	}
}
void solve(int L,int R,int ql,int qr){
	//printf("solve: (%d %d) (%d %d)\n",L,R,ql,qr);
	if(ql>qr) return;
	if(L==R){
		for(int i=ql;i<=qr;i++){
			if(q[i].op==2) ans[q[i].id]=L;
		}
		return;
	}
	int mid=(L+R)>>1;
	int l=ql,r=ql;
	while(q[r].op==1&&r<=qr) ++r;
	while(r<=qr){
		while(l<=qr&&q[l].op==1&&q[l].h<=q[r].x){
			if(q[l].w<=mid){
				add(q[l].l,q[l].f);
				add(q[l].r+1,-q[l].f);	
				//printf("  add:l=%d (%d %d) f=%d\n",l,q[l].l,q[l].r,q[l].f);
			}
			++l;
		}
		w[q[r].id]+=ask(q[r].y);
		++r;
	}
	int pl=ql,n1(0),n2(0);
	for(;pl<=qr&&q[pl].op==1;++pl){
		if(q[pl].w<=mid){
			q1[++n1]=q[pl];
			if(pl<l){
				add(q[pl].l,-q[pl].f);
				add(q[pl].r+1,q[pl].f);	
				//printf("  del:pl=%d (%d %d) f=%d\n",pl,q[pl].l,q[pl].r,q[pl].f);
			}
		}
		else q2[++n2]=q[pl];
	}
	for(;pl<=qr;pl++){
		int c=w[q[pl].id];
		if(c>=q[pl].k) q1[++n1]=q[pl];
		else{
			q[pl].k-=c;q2[++n2]=q[pl];
		}
	}
	for(int i=ql;i<=qr;i++){
		if(q[i].op==2) w[q[i].id]=0;
	}
	for(int i=1;i<=n1;i++) q[ql+i-1]=q1[i];
	for(int i=1;i<=n2;i++) q[ql+n1+i-1]=q2[i];
	solve(L,mid,ql,ql+n1-1);solve(mid+1,R,ql+n1,qr);
	return;
}
signed main(){
#ifndef ONLINE_JUDGE
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
#endif
	//printf("%d\n",(int)sizeof(tr)/1024/1024);
	memset(fi,-1,sizeof(fi));cnt=-1;
	n=read();m=read();Q=read();
	for(int i=1;i<n;i++){
		int x=read(),y=read();
		addline(x,y);addline(y,x);
	}
	dfs(1,0);
	for(int i=1;i<=m;i++){
		int x=read(),y=read(),w=read(),lca=Lca(x,y,1);
		//printf("i=%d (%d %d)\n",i,x,y);
		if(lca!=x) swap(x,y);
		if(lca==x){
			int tp=Lca(y,x,0);
			q[++tot]=(ope){1,0,0,0,0,pos[y],1,pos[tp]-1,w,1};
			//print(q[tot]);
			q[++tot]=(ope){1,0,0,0,0,pos[y],pos[tp]+siz[tp],n,w,1};
			//print(q[tot]);
			q[++tot]=(ope){1,0,0,0,0,pos[y]+siz[y],1,pos[tp]-1,w,-1};
			//print(q[tot]);
			q[++tot]=(ope){1,0,0,0,0,pos[y]+siz[y],pos[tp]+siz[tp],n,w,-1};
			//print(q[tot]);
		}		
		else{
			q[++tot]=(ope){1,0,0,0,0,pos[y],pos[x],pos[x]+siz[x]-1,w,1};
			//print(q[tot]);
			q[++tot]=(ope){1,0,0,0,0,pos[y]+siz[y],pos[x],pos[x]+siz[x]-1,w,-1};
			//print(q[tot]);
		}
	}
	int pp=tot;
	for(int i=1;i<=Q;i++){
		int x=read(),y=read(),k=read();
		q[++tot]=(ope){2,pos[x],pos[y],k,i,0,0,0,0,0};
		q[++tot]=(ope){2,pos[y],pos[x],k,i,0,0,0,0,0};
	}
	sort(q+1,q+pp+1,cmp1);
	sort(q+pp+1,q+1+tot,cmp2);
	solve(0,1e9,1,tot);
	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、付费专栏及课程。

余额充值