[HNOI2015]接水果——整体二分

题目大意:

给定一颗树和m条带有权值的路径以及q个路径询问,每次求m条路径中且为给出的路径的子路径的第k小。

思路:

首先考虑怎么判断一条路经是不是一条路径的子路径,假设这条路径在树上拐弯,那么包含它的路径的两个端点要分别在两个子树里面,如果这条路径在树上不拐弯,假设(v)是深度大的那个,(u)是深度小的那个,那么包含它的路径要有一个端点在(v)的子树内,另外一个端点在(u)连接(v)的那个儿子的子树外。
于是上面的条件都可以用dfs序来判定,把每一条路径表示成一个二维平面上的点((u,v)),每一个子路径代表的范围可以变成一个或者两个矩形,一条路径包含另外一条路径当且仅当这条路径在子路径所表示的矩形内。
于是树上的问题变成了平面上的问题,考虑整体二分,每次check变成了计算包含一个点的矩形的数量,直接扫描线即可计算即可。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define MREP(i,x) for(int i=beg[x],v;v=to[i],i;i=las[i])
#define debug(x) cout<<#x<<"="<<x<<endl
#define fi first
#define se second
#define mk make_pair
#define pb push_back
#define y1 wtcl
typedef long long ll;

using namespace std;

void File(){
	freopen("luogu3242.in","r",stdin);
	freopen("luogu3242.out","w",stdout);
}

template<typename T>void read(T &_){
	T __=0,mul=1; char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')mul=-1;
		ch=getchar();
	}
	while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
	_=__*mul;
}

const int maxn=40000+10;
int n,m,q,b[maxn],tot,cnt,ans[maxn],st[maxn][21],Log[maxn],dep[maxn];
int L[maxn],cnt_dfn,R[maxn],sz[maxn];
int beg[maxn],to[maxn<<1],las[maxn<<1],cnte=1;

void add(int u,int v){
	las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v;
	las[++cnte]=beg[v]; beg[v]=cnte; to[cnte]=u;
}

void dfs_init(int u,int f){
	sz[u]=1;
	st[u][0]=f;
	dep[u]=dep[f]+1;
	L[u]=++cnt_dfn;
	MREP(i,u)if(v!=f){
		dfs_init(v,u);
		sz[u]+=sz[v];
	}
	R[u]=L[u]+sz[u]-1;
}

struct Query{
	int x,y,y1,y2,val,id,ty;
	//ty=0 fruit
	//ty=1 add to the BIT
	//ty=-1 erase from the BIT
	bool operator < (const Query & tt) const {
		if(x!=tt.x)return x<tt.x;
		return abs(ty)>abs(tt.ty);
	}
}qu[maxn<<3],t1[maxn<<3],t2[maxn<<3];

int find(int x,int y){
	DREP(i,Log[dep[x]-dep[y]],0)
		if(dep[st[x][i]]>dep[y])
			x=st[x][i];
	return x;
}

void init(){
	read(n),read(m),read(q);
	REP(i,2,n)Log[i]=Log[i/2]+1;

	int u,v,val;
	REP(i,1,n-1)read(u),read(v),add(u,v);
	dfs_init(1,0);

	REP(j,1,20)REP(i,1,n)
		if((1<<j)<=dep[i]-1)
			st[i][j]=st[st[i][j-1]][j-1];

	REP(i,1,m){
		read(u),read(v),read(val);
		b[++tot]=val;

		if(L[u]>L[v])swap(u,v);//L[u]<L[v]

		if(R[u]<L[v]){
			qu[++cnt]=(Query){L[u],0,L[v],R[v],val,0,1};
			qu[++cnt]=(Query){R[u]+1,0,L[v],R[v],val,0,-1};
		}
		else{
			u=find(v,u);
			qu[++cnt]=(Query){1,0,L[v],R[v],val,0,1};
			qu[++cnt]=(Query){L[u],0,L[v],R[v],val,0,-1};
			qu[++cnt]=(Query){L[v],0,R[u]+1,n,val,0,1};
			qu[++cnt]=(Query){R[v]+1,0,R[u]+1,n,val,0,-1};
		}
	}

	sort(b+1,b+tot+1);
	tot=unique(b+1,b+tot+1)-b-1;
	REP(i,1,cnt)qu[i].val=lower_bound(b+1,b+tot+1,qu[i].val)-b;

	REP(i,1,q){
		read(u),read(v),read(val);
		if(L[u]>L[v])swap(u,v);
		//cout<<L[u]<<" "<<L[v]<<endl;
		qu[++cnt]=(Query){L[u],L[v],0,0,val,i,0};
	}

	sort(qu+1,qu+cnt+1);
	//REP(i,1,cnt)printf("%d %d %d %d
",qu[i].x,qu[i].y1,qu[i].y2,qu[i].ty);
}

struct BIT{
	int sum[maxn];
	int lowbit(int x){return x&(-x);}
	void add(int p,int x){for(;p<=n;p+=lowbit(p))sum[p]+=x;}
	int query(int p){int ret=0;for(;p>=1;p-=lowbit(p))ret+=sum[p];return ret;}
}T;

void solve(int ql,int qr,int l,int r){
	if(ql>qr || l>r)return;
	if(l==r){REP(i,ql,qr)if(!qu[i].ty)ans[qu[i].id]=b[l];return;}

	int c1=0,c2=0,mid=(l+r)>>1;

	REP(i,ql,qr)if(qu[i].ty){
		if(qu[i].val<=mid)t1[++c1]=qu[i];
		else t2[++c2]=qu[i];

		if(qu[i].val<=mid){
			T.add(qu[i].y1,qu[i].ty);
			T.add(qu[i].y2+1,-qu[i].ty);
		}
		//printf("%d %d %d %d
",qu[i].x,qu[i].y1,qu[i].y2,qu[i].ty);
	}
	else{
		int sum=T.query(qu[i].y);
		//printf("%d %d %d
",qu[i].x,qu[i].y,sum);
		if(qu[i].val<=sum)t1[++c1]=qu[i];
		else t2[++c2]=qu[i],t2[c2].val-=sum;
	}

	REP(i,1,c1)if(t1[i].ty){
		T.add(t1[i].y1,-t1[i].ty);
		T.add(t1[i].y2+1,t1[i].ty);
	}

	REP(i,1,c1)qu[ql+i-1]=t1[i];
	REP(i,1,c2)qu[ql+i+c1-1]=t2[i];

	solve(ql,ql+c1-1,l,mid);
	solve(ql+c1,qr,mid+1,r);
}

int main(){
	File();
	init();
	solve(1,cnt,1,tot);
	REP(i,1,q)printf("%d
",ans[i]);
	return 0;
}

原文地址:https://www.cnblogs.com/ylsoi/p/10104287.html