【整体二分/点分治/树链剖分 + 数据结构】BZOJ4009 [HNOI2015]接水果

【题目】
BZOJ
一棵 n n n个节点的树,有 P P P条带权值不相同路径, Q Q Q个询问。每个询问给定一条路径以及一个 K K K,查询所有带权路径中是给定路径的子路径的第 K K K小权值。 n , m , Q ≤ 4 × 1 0 4 n,m,Q\leq 4\times 10^4 n,m,Q4×104

【解题思路】
首先显然的套路是一条带权路径能贡献到的询问是询问路径的两个端点分别在带权路径的两个子树中或一个在子树中另一个不在子树中。(哎呀常用的东西看起来很长而已)

那么一条路径可以贡献到的范围用一个二维矩形来表示,现在问题就是求覆盖一个点的矩形中第 K K K小的权值。
不妨先将矩形权值从小到大排序,然后扫描线一波,这样子问题就是每次在 [ l , r ] [l,r] [l,r]区间的每个位置插入或删除一个数 x x x,或者询问一个位置 x x x上第 k k k小的数,可以用树套树维护这个问题,第一维值域,第二维位置。

修改时,在外层线段树的根一直走到 x x x表示的叶子节点,将每个节点内部线段树的区间 [ l , r ] [l,r] [l,r]进行 + 1 +1 +1 + 1 +1 +1,显然是 O ( log ⁡ 2 n ) O(\log ^2 n) O(log2n)的。
询问时,在外层线段树上二分即可。由于每走到一个节点需要在内层线段树上查一次,因此是 O ( log ⁡ 2 n ) O(\log ^2 n) O(log2n)的。

总的复杂度 O ( n log ⁡ 2 n ) O(n\log ^2 n) O(nlog2n)

这个问题用整体二分也是可以做的,思想一样,由于总的扫描线数是 O ( m log ⁡ m ) O(m\log m) O(mlogm)的,那么加上整体二分的 log ⁡ \log log,复杂度是一样的。但整体二分的第二维可以用 BIT \text{BIT} BIT,因此常数优秀很多。

emmm还可以用主席树做到在线询问,同上处理出矩形后,对于第一维,我们可以在树上建主席树,以 a , b a,b a,b两端点对应的链就是常见的差分: r t a + r t b − r t l c a ( a , b ) − r t ( f a l c a ( a , b ) ) rt_a+rt_b-rt_{lca(a,b)}-rt(fa_{lca(a,b)}) rta+rtbrtlca(a,b)rt(falca(a,b))。对于第二维由于问题有可加减性,所以可以用 DFS \text{DFS} DFS序维护。复杂度是一样的。

当然你也可以用点分治来做这题,复杂度还是一样的。

【参考代码】
整体二分

#include
using namespace std;

const int N=8e4+10;
int n,m,Q;

namespace IO
{
	int read()
	{
		int ret=0;char c=getchar();
		while(!isdigit(c)) c=getchar();
		while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
		return ret;
	}
	void write(int x){if(x>9)write(x/10);putchar(x%10^48);}
	void writeln(int x){write(x);putchar('\n');}
}
using namespace IO;

namespace BIT
{
#define lowbit(x) (x&(-x))
	struct BIT
	{
		int lim,c[N];
		void init(int x){lim=x;}
		void upd(int x,int v){for(;x<=lim;x+=lowbit(x))c[x]+=v;}
		void update(int l,int r,int v){upd(l,v);upd(r+1,-v);}
		int query(int x){int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
	}tr;
#undef lowbit
}
using namespace BIT;

namespace Tree
{
	int tot,ind;
	int head[N],st[N],ed[N],dep[N],fa[18][N],fc[20];
	struct Tway{int v,nex;}e[N];
	void add(int u,int v)
	{
		e[++tot]=(Tway){v,head[u]};head[u]=tot;
		e[++tot]=(Tway){u,head[v]};head[v]=tot;
	}
	void initst(){fc[0]=1;for(int i=1;i<19;++i)fc[i]=fc[i-1]<<1;}
	void dfs(int x)
	{
		st[x]=++ind;
		for(int i=1;fc[i]<=dep[x];++i) fa[i][x]=fa[i-1][fa[i-1][x]];
		for(int i=head[x];i;i=e[i].nex)
		{
			int v=e[i].v;
			if(v==fa[0][x]) continue;
			fa[0][v]=x;dep[v]=dep[x]+1;dfs(v);
		}
		ed[x]=ind;
	}
	int jump(int x,int t)
	{
		for(int i=0;i<18;++i) 
			if(t&fc[i]) x=fa[i][x];
		return x;
	}
	int lca(int x,int y)
	{
		if(dep[x]<dep[y]) swap(x,y);
		x=jump(x,dep[x]-dep[y]);
		for(int i=17;~i;--i) if(fa[i][x]^fa[i][y])
			x=fa[i][x],y=fa[i][y];
		return x==y?x:fa[0][x]; 
	}
}
using namespace Tree;

namespace Overall_Divide
{
	int n,m,Q,sz,cnt,ans[N],sum[N];
	struct data
	{
		int xl,xr,yl,yr,w;
		data(int _xl=0,int _xr=0,int _yl=0,int _yr=0,int _w=0):xl(_xl),xr(_xr),yl(_yl),yr(_yr),w(_w){}
		bool operator <(const data&rhs)const{return w<rhs.w;}
		void out(){printf("%d %d %d %d %d\n",xl,xr,yl,yr,w);}
	}mt[N];
	struct event
	{
		int x,yl,yr,w,id;
		event(int _x=0,int _yl=0,int _yr=0,int _w=0,int _id=0):x(_x),yl(_yl),yr(_yr),w(_w),id(_id){}
		bool operator <(const event&rhs)const{return x==rhs.x?id<rhs.id:x<rhs.x;}
		void out(){printf("%d %d %d %d %d\n",x,yl,yr,w,id);}
	}ev[N];
	struct Tquery
	{
		int x,y,k,id;
		Tquery(int _x=0,int _y=0,int _k=0,int _id=0):x(_x),y(_y),k(_k),id(_id){}
	}qry[N],tmpq[N];
	void solve(int l,int r,int L,int R)
	{
		//printf("%d %d %d %d\n",l,r,L,R);
		//printf("ids:");for(int i=L;i<=R;++i) printf("%d ",qry[i].id); puts("");
		if(L>R) return;
		if(l==r)
		{
			for(int i=L;i<=R;++i) ans[qry[i].id]=mt[l].w;
			return;
		}
		int mid=(l+r)>>1;sz=0;
		for(int i=l;i<=mid;++i)
		{
			ev[++sz]=event(mt[i].xl,mt[i].yl,mt[i].yr,1,0);
			ev[++sz]=event(mt[i].xr,mt[i].yl,mt[i].yr,-1,n+1);
		}
		for(int i=L;i<=R;++i) ev[++sz]=event(qry[i].x,qry[i].y,0,0,i);
		sort(ev+1,ev+sz+1);
		//puts("begin");
		//for(int i=1;i<=sz;++i) ev[i].out();
		//puts("end");
		for(int i=1;i<=sz;++i) 
			if(L<=ev[i].id && ev[i].id<=R) sum[ev[i].id]=tr.query(ev[i].yl);
			else tr.update(ev[i].yl,ev[i].yr,ev[i].w);
		int tl=L-1,tr=R+1;
		for(int i=L;i<=R;++i)
			if(sum[i]>=qry[i].k) tmpq[++tl]=qry[i];
			else tmpq[--tr]=qry[i],tmpq[tr].k-=sum[i];
		for(int i=L;i<=R;++i) qry[i]=tmpq[i];
		solve(l,mid,L,tl);solve(mid+1,r,tr,R);
	}
	void init()
	{
		n=read();m=read();Q=read();tr.init(n);
		for(int i=1;i<n;++i) add(read(),read());
		initst();dfs(1);
		for(int i=1;i<=m;++i)
		{
			int a=read(),b=read(),c=read(),u=lca(a,b);
			if(st[a]>st[b]) swap(a,b);
			if(u^a) mt[++cnt]=data(st[a],ed[a],st[b],ed[b],c);
			else
			{
				int v=jump(b,dep[b]-dep[a]-1);
				mt[++cnt]=data(1,st[v]-1,st[b],ed[b],c);
				if(ed[v]<n) mt[++cnt]=data(st[b],ed[b],ed[v]+1,n,c);
			}
		}
		sort(mt+1,mt+cnt+1);
		//for(int i=1;i<=cnt;++i) mt[i].out();
		for(int i=1;i<=Q;++i)
		{
			int a=read(),b=read(),k=read();
			if(st[a]>st[b]) swap(a,b);
			qry[i]=Tquery(st[a],st[b],k,i);
		}
	}
	void solution()
	{
		init();solve(1,cnt,1,Q);
		for(int i=1;i<=Q;++i) writeln(ans[i]);
	}
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("BZOJ4009.in","r",stdin);
	freopen("BZOJ4009.out","w",stdout);
#endif
	Overall_Divide::solution();
	return 0;
}

你可能感兴趣的:(分而治之-整体二分,其他-扫描线)