【Luogu】 P3242 [HNOI2015] 接水果

题目链接

点击打开链接

题目解法

我们发现一段路径包含另一条不好做,但是被包含的关系就比较简单

对于每个盘子,我们可以分成 2 类情况,这里可以预处理出dfn序,令st[u]为进入u的时间,ed[u]为出去u的时间

  1. lca(a,b)=a(这里假设a是b的祖先),令k为a的儿子,且为b的祖先
    画图可以发现,a-b被包含当且仅当另一条路径一个点在[ st[b],ed[b] ],即b的子树中
    另一个点在[ 1,st[k]-1 ] 或 [ ed[k]+1,n ] 中,即不在k的子树中
  2. lca(a,b)≠a且lca(a,b)≠b
    a-b被包含当且仅当另一条路径一个点在[ st[a],ed[a] ],即a的子树中
    另一个点在[ st[b],ed[b] ],即b的子树中

上面的东西可以用树套树维护一下,询问的时候二分一下,时间复杂度 O(nlog^{3}n)

考虑更优的做法

我们发现每次询问都会进行二分,于是可以采用整体二分的思想

我们发现每个盘子会对1-2个矩阵做出贡献,那么可以考虑用扫描线

时间复杂度 O(nlog^{2}n)

这启发我们一些树套树2个区间的问题可以转化为矩阵的问题进行求解

#include <bits/stdc++.h>
#define lowbit(x) x&-x
#define swap(x,y) x^=y^=x^=y
using namespace std;
const int N(40100),P(40100),Q(40100),inf(1e9+5);
struct Modify{
	int col,l,r,c,t;
}modify[P<<2],mL[P<<2],mR[P<<2];
struct Query{
	int u,v,k,id,ans;
}query[Q],qL[Q],qR[Q];
int n,p,q,ANS[Q];
int dfn[N],idx,siz[N],depth[N],up[N][18];
vector<int> vec[N];
struct BIT{
	int tr[N];
	void add(int x,int k){
		for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=k;
	}
	int ask(int x){
		int res=0;
		for(int i=x;i;i-=lowbit(i)) res+=tr[i];
		return res;
	}
}bit;
inline int read(){
	int FF=0,RR=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar())
		if(ch=='-')
			RR=-1;
	for(;isdigit(ch);ch=getchar())
		FF=(FF<<1)+(FF<<3)+ch-48;
	return FF*RR;
}
void dfs(int u){
	dfn[u]=++idx,siz[u]=1;
	for(int i=0;i<vec[u].size();i++){
		int v=vec[u][i];
		if(v!=up[u][0]){
			depth[v]=depth[u]+1,up[v][0]=u;
			dfs(v);
			siz[u]+=siz[v];
		}
	}
}
void solve(int l,int r,int Lm,int Rm,int Lq,int Rq){
	if(Lq>Rq) return;
	if(l==r){
		for(int i=Lq;i<=Rq;i++) query[i].ans=l;
		return;
	}
	int mid=(l+r)>>1;
	int Msiz1=0,Msiz2=0;
	for(int i=Lm;i<=Rm;i++){
		if(modify[i].c<=mid) mL[++Msiz1]=modify[i];
		else mR[++Msiz2]=modify[i];
	}
	for(int i=1;i<=Msiz1;i++) modify[i+Lm-1]=mL[i];
	for(int i=1;i<=Msiz2;i++) modify[i+Lm+Msiz1-1]=mR[i];
//	cout<<Lm<<' '<<Rm<<'\n';
//	cout<<l<<' '<<r<<" : "<<Msiz1<<'\n';
	int Qsiz1=0,Qsiz2=0,j=1;
	for(int i=Lq;i<=Rq;i++){
		while(j<=Msiz1&&mL[j].col<=query[i].u){
			bit.add(mL[j].l,mL[j].t);
			bit.add(mL[j].r+1,-mL[j].t);
			j++;
		}
		int res=bit.ask(query[i].v);
		if(res>=query[i].k) qL[++Qsiz1]=query[i];//往l-mid走 
		else qR[++Qsiz2]=query[i],qR[Qsiz2].k-=res;//往mid+1-r走 
	}
	for(int i=1;i<j;i++){
		bit.add(mL[i].l,-mL[i].t);
		bit.add(mL[i].r+1,mL[i].t);
	}
	for(int i=1;i<=Qsiz1;i++) query[i+Lq-1]=qL[i];
	for(int i=1;i<=Qsiz2;i++) query[i+Lq+Qsiz1-1]=qR[i];
	solve(l,mid,Lm,Lm+Msiz1-1,Lq,Lq+Qsiz1-1);
	solve(mid+1,r,Lm+Msiz1,Rm,Lq+Qsiz1,Rq);
}
bool cmp1(const Modify &x,const Modify &y){
	return x.col<y.col;
}
bool cmp2(const Query &x,const Query &y){
	return x.u<y.u;
}
int get_lca(int x,int y){
	if(depth[x]>depth[y]) swap(x,y);
	for(int i=16;i>=0;i--)
		if(depth[up[y][i]]>depth[x]) y=up[y][i];
	if(up[y][0]==x) return y;
	y=up[y][0];
	for(int i=16;i>=0;i--)
		if(up[x][i]!=up[y][i]) x=up[x][i],y=up[y][i];
	return x;
}
int main(){
	n=read(),p=read(),q=read();
	for(int i=1,x,y;i<n;i++){
		x=read(),y=read();
		vec[x].push_back(y),vec[y].push_back(x);
	}
	dfs(1);
	for(int j=1;j<=16;j++)
		for(int i=1;i<=n;i++)
			up[i][j]=up[up[i][j-1]][j-1];
	int tot=0;
	for(int i=1,a,b,c;i<=p;i++){
		a=read(),b=read(),c=read();
		if(dfn[a]<dfn[b]) swap(a,b);//保证dfn[b]<dfn[a] 
		int below=get_lca(a,b),lca=up[below][0];
		if(lca!=a&&lca!=b){//a,b不成祖孙关系 
			modify[++tot]={dfn[b],dfn[a],dfn[a]+siz[a]-1,c,1};
			modify[++tot]={dfn[b]+siz[b],dfn[a],dfn[a]+siz[a]-1,c,-1};
		}
		else{
			modify[++tot]={1,dfn[a],dfn[a]+siz[a]-1,c,1};
			modify[++tot]={dfn[below],dfn[a],dfn[a]+siz[a]-1,c,-1};
			if(dfn[below]+siz[below]<=n){
				modify[++tot]={dfn[a],dfn[below]+siz[below],n,c,1};
				modify[++tot]={dfn[a]+siz[a],dfn[below]+siz[below],n,c,-1};
			}
		}
	}
	sort(modify+1,modify+tot+1,cmp1);
//	for(int i=1;i<=tot;i++)
//		cout<<modify[i].col<<' '<<modify[i].l<<' '<<modify[i].r<<' '<<modify[i].c<<' '<<modify[i].t<<'\n'; 
	for(int i=1,u,v,k;i<=q;i++){
		u=read(),v=read(),k=read();
		if(dfn[u]>dfn[v]) swap(u,v);
		query[i]={dfn[u],dfn[v],k,i};
	}
	sort(query+1,query+q+1,cmp2);
	solve(1,inf,1,tot,1,q);
	for(int i=1;i<=q;i++) ANS[query[i].id]=query[i].ans;
	for(int i=1;i<=q;i++) printf("%d\n",ANS[i]);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值