zoukankan      html  css  js  c++  java
  • 【BZOJ4009】[HNOI2015] 接水果(二维数点)

    点此看题面

    大致题意: 给定一棵无根树,选定若干条树上路径(盘子)并分别给出权值,每次询问一条树上路径(水果)包含的路径(盘子)中第(k)小的权值。

    前言

    这题还是比较水的,一眼就看出来是二维数点,不过想一个比较容易写的方法想了挺久(一开始还想着写树套树)。

    所以为什么是水果的路径包含盘子的路径才能被接到。。。

    二维数点

    考虑树上路径包含,可以转化为(dfs)序的大小关系。

    设有一个盘子((u,v))(设(dI_u<dI_v)),它能接到的水果((x,y))(设(dI_x<dI_y))必然要满足某种条件,而这需要分两类讨论:

    • (u)(v)不互为祖先关系,则(x)应在(u)的子树内,(y)应在(v)的子树内,即(dI_ule dI_xle dO_u,dI_vle dI_yle dO_v)
    • (u)(v)的祖先,则设(w)为子树中包含(v)的那个(u)的子节点,那么只要(x,y)只要有一个在(w)子树外,一个在(v)子树内即可,因此有(1le dI_x< dI_w,dI_vle dI_yle dO_v)(dI_vle dI_xle dO_v,dO_w<yle n)

    这东西如果考虑树套树,因为问的是第(k)大,那么就需要主席树套主席树,想想都非常可怕。

    但是,众所周知,树套树的题目,强制在线的可以分块爆踩(hl666曰),不强制在线的可以CDQ分治、整体二分乱搞。

    因此,对于这题我们考虑整体二分,二分当前答案,则只要知道有多少个小于等于当前答案的点即可。

    考虑每个盘子贡献的是一个矩形(或两个),那么就可以用扫描线来做。

    这个做法就非常容易实现了。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 40000
    #define LN 20
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    #define swap(x,y) (x^=y^=x^=y)
    using namespace std;
    int n,P,Q,dc,dv[N+5],ee,lnk[N+5];struct edge {int to,nxt;}e[N<<1];struct Pl{int x,y,v;}p[N+5];
    struct Qry
    {
    	int id,x,y,k;I bool operator < (Con Qry& o) Con {return x<o.x;}
    }q[N+5];
    struct Seg
    {
    	int x,l,r,v;I Seg(CI p=0,CI a=0,CI b=0,CI t=0):x(p),l(a),r(b),v(t){}
    	I bool operator < (Con Seg& o) Con {return x<o.x;}
    }s[4*N+5];int cnt;
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define pc(c) (C==E&&(clear(),0),*C++=c)
    		#define D isdigit(c=tc())
    		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
    	public:
    		I FastIO() {A=B=FI,C=FO,E=FO+FS;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
    		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    		Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('
    ');}
    		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
    }F;
    struct Tree
    {
    	int d,dI[N+5],dO[N+5],dep[N+5],f[N+5][LN+5];
    	I void dfs(CI x=1)//遍历
    	{
    		RI i;for(dI[x]=++d,i=1;i<=LN;++i) f[x][i]=f[f[x][i-1]][i-1];//预处理倍增数组
    		for(i=lnk[x];i;i=e[i].nxt) !dI[e[i].to]&&
    			(dep[e[i].to]=dep[f[e[i].to][0]=x]+1,dfs(e[i].to),0);dO[x]=d;
    	}
    	I bool IsFa(CI x,CI y) {return dI[x]<=dI[y]&&dI[y]<=dO[x];}//判断是不是祖先(其实就是判在不在子树内)
    	I int Jump(CI x,RI y)//倍增跳儿子
    	{
    		for(RI i=0,d=dep[x]+1;d^dep[y];++i) (d^dep[y])>>i&1&&(y=f[y][i]);return y;
    	}
    }T;
    struct BIT//树状数组
    {
    	int a[N+5];I void U(RI x,CI v) {W(x<=n+1) a[x]+=v,x+=x&-x;}
    	I int Q(RI x,RI t=0) {W(x) t+=a[x],x-=x&-x;return t;}
    }B;
    class WholeSolver//整体二分
    {
    	private:
    		Seg tl[N+5],tr[N+5];Qry kl[N+5],kr[N+5];
    	public:
    		int ans[N+5];
    		I void Solve(CI l,CI r,CI sl,CI sr,CI ql,CI qr)//整体二分
    		{
    			RI i,j,ul=0,ur=0,vl=0,vr=0;
    			if(l==r||ql>qr) {for(i=ql;i<=qr;++i) ans[q[i].id]=l;return;}//边界
    			RI mid=l+r>>1;for(i=sl,j=ql;j<=qr;++j)
    			{
    				W(i<=sr&&s[i].x<=q[j].x) abs(s[i].v)>mid?tr[++ur]=s[i++]//如果值大于当前二分的答案
    					:(B.U(s[i].l,s[i].v>0?1:-1),B.U(s[i].r+1,s[i].v>0?-1:1),tl[++ul]=s[i++]);//树状数组上修改
    				B.Q(q[j].y)>=q[j].k?(kl[++vl]=q[j],0):(kr[++vr]=q[j]).k-=B.Q(q[j].y);//判断询问扔哪边
    			}
    			W(i<=sr) abs(s[i].v)>mid?tr[++ur]=s[i++]//对剩余修改分组,完成所有修改也就完成了清空
    				:(B.U(s[i].l,s[i].v>0?1:-1),B.U(s[i].r+1,s[i].v>0?-1:1),tl[++ul]=s[i++]);
    			for(i=1;i<=ul;++i) s[sl+i-1]=tl[i];for(i=1;i<=ur;++i) s[sl+ul+i-1]=tr[i];
    			for(i=1;i<=vl;++i) q[ql+i-1]=kl[i];for(i=1;i<=vr;++i) q[ql+vl+i-1]=kr[i];
    			Solve(l,mid,sl,sl+ul-1,ql,ql+vl-1),Solve(mid+1,r,sl+ul,sl+ul+ur-1,ql+vl,ql+vl+vr-1);//递归
    		}
    }S;
    int main()
    {
    	RI i,x,y;for(F.read(n,P,Q),i=1;i^n;++i) F.read(x,y),add(x,y),add(y,x);T.dfs();
    	for(i=1;i<=P;++i) F.read(p[i].x,p[i].y,p[i].v),dv[i]=p[i].v;sort(dv+1,dv+P+1);
    	for(dc=unique(dv+1,dv+P+1)-dv-1,i=1;i<=P;++i)
    		p[i].v=lower_bound(dv+1,dv+dc+1,p[i].v)-dv,T.dI[p[i].x]>T.dI[p[i].y]&&swap(p[i].x,p[i].y),//离散化
    		#define Add(a,b,c,d,v) (c<=d&&(s[++cnt]=Seg(a,c,d,v),s[++cnt]=Seg(b+1,c,d,-v),0))//加一个矩形
    		!T.IsFa(p[i].x,p[i].y)?Add(T.dI[p[i].x],T.dO[p[i].x],T.dI[p[i].y],T.dO[p[i].y],p[i].v)//不互为祖先关系
    		:(x=T.Jump(p[i].x,p[i].y),Add(1,T.dI[x]-1,T.dI[p[i].y],T.dO[p[i].y],p[i].v),//互为祖先关系
    		Add(T.dI[p[i].y],T.dO[p[i].y],T.dO[x]+1,n,p[i].v));sort(s+1,s+cnt+1);//最后记得排序
    	for(i=1;i<=Q;++i) F.read(q[i].x,q[i].y,q[i].k),q[i].id=i,//读入询问
    		(q[i].x=T.dI[q[i].x])>(q[i].y=T.dI[q[i].y])&&swap(q[i].x,q[i].y);sort(q+1,q+Q+1);//记得排序
    	for(S.Solve(1,dc,1,cnt,1,Q),i=1;i<=Q;++i) F.writeln(dv[S.ans[i]]);return F.clear(),0;//整体二分后输出答案
    }
    
  • 相关阅读:
    CUDA内存介绍
    CUDA10.0 官方手册 阅读笔记 章三 CUDA编程接口
    Texture Gather 讲解
    cuda学习--纹理内存
    计算机缓存Cache以及Cache Line详解
    nvidia的cuda编程api
    将ORBSLAM往ANDROID STUDIO 移植的时候一些坑
    解决Android10读取不到/sdcard/、/storage/emulated/0/文件的问题
    Android NDK 从入门到精通(汇总篇)
    好好说说c++内存序--以单例模式为例子
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4009.html
Copyright © 2011-2022 走看看