• [题解] LuoguP6071 [MdOI2020] Treequery


    传送门

    感觉这是一个写的很舒服的题?

    树上路径的交什么的就很想树上差分?发现根本没法做...它还要求在线....


    好先来看(Subtask)(qwq)...

    Subtask 1

    (l=r),就是每次询问树上两点之间的距离...这个(LCA)啥的搞一搞就好了。

    目前得分(8)分。。。

    Subtask 2

    (p = 1)?也就是说(l...r)这些点都在(p)里面,脑补一下答案就是(p)(LCA(l...r))的距离。

    结合Subtask1,就可以拿到(28)分的好成绩......

    Subtask 3

    (n le 10^3)。。。怎么暴力怎么写吧。。。

    我们目前收获了(48)的好成绩...

    Subtask 4

    (n le 10^5)。。。并不知道满分做法以外的做法。。。

    然后放弃了希望,48分跑路。。。


    (100)分的方法吧(下面默认以(1)号结点为根)

    (Subtask2)感觉很有启发......如果(l...r)都在(p)的字数内的话,那么答案就是(dist(p, LCA(l..r)))(dist(x,y))表示树上(x,y)的距离)

    那如果不在呢?

    显然如果(l...r)一部分在(p)的子树内,一部分不在的话答案就是(0)

    所以只会有两种情况

    • (l...r)全部在(p)的子树内,答案为(dist(p, LCA(l..r)))
    • (l...r)全部在(p)的子树外

    来想第二种好了。

    答案也是(dist(p,LCA(l..r)))吗?显然毒瘤出题人不可能让你秒了题没有这么简单。

    丑图警告!

    由上面那张图可以知道,这时候的答案是(p)(LCA(p,l),LCA(p,l+1)...LCA(p,r))这些节点的最短距离(也就是到这(r-l+1)个点中深度最大的点的距离)

    这样就对了吗?没有。。。

    下面为了方便令(x)表示(LCA(p,l),LCA(p,l+1)...LCA(p,r))中深度最大的点。

    注意到上图,这些(l..r)所在的子树中有两棵挂在(p)到根的链上,那如果必定要跨越更节点呢?

    脑补一下可以知道,这时候(LCA(l..r))的深度一定是大于(x)的深度的,而上面的情况则是小于。

    所以这个情况下答案是(dist(p,LCA(l..r)))

    总结一下,如果(l...r)都在(p)的子树外面,那么答案就是点(p)(LCA(p,l),LCA(p,l+1)...LCA(p,r))(LCA(l...r))这些节点中深度最大的节点的距离。


    好,分类讨论了一波我们似乎得到了结论,但咋写啊。。。

    考虑如何查询一个点是否在另一个点的子树内

    因为(Dfs)序中,一个子树对应一个区间,我们只要查询这个区间内是否包含某些点就好了。

    (Dfs)序建出主席树就好啦!

    我们还要询问(l...r)这些节点的(lca),因为(lca)也是可合并的,所以用线段树查询。

    等等。。。那还要查(LCA(p,l),LCA(p,l+1)...LCA(p,r))中深度最大的点啊。。。

    这个有两种方法,一种是倍增向上跳父亲,然后主席树(check)

    另一种是树剖过后向上跳链,然后对每个重链二分,同样的主席树(check)

    第二种方法应该会比第一种快。。。没写。。。还是写了倍增。。。

    但是我lca是树剖求的啊。。。亏了qwq

    总复杂度(O(n log^2 n))

    所以下面是一个憨批写的又长又慢,(lca)用了树剖,但最后查询是用倍增的超大常数代码。。。

    #include <bits/stdc++.h>
    using namespace std;
    const int N=2e5+10;
    struct edge{
    	int to,nxt,w;
    }e[N<<1];
    int fst[N],cnt;
    inline void ade(int x,int y,int w){
    	e[++cnt]=(edge){y,fst[x],w},fst[x]=cnt;
    }
    inline void addedge(int x,int y,int w){
    	ade(x,y,w),ade(y,x,w);
    }
    int dis[N],dfn[N],id[N],size[N],fa[N],top[N],son[N],dep[N],idx=0,anc[N][23];
    void dfs1(int x,int prev,int deep,int dd){
    	dfn[x]=++idx,id[idx]=x,dep[x]=deep,size[x]=1;
    	anc[x][0]=fa[x]=prev,size[son[x]=0]=-1,dis[x]=dd;
    	for(int i=1;i<=18;i++)
    		anc[x][i]=anc[anc[x][i-1]][i-1];
    	for(int i=fst[x];i;i=e[i].nxt){
    		int v=e[i].to; if(v==prev) continue;
    		dfs1(v,x,deep+e[i].w,dd+1),size[x]+=size[v];
    		if(size[v]>size[son[x]]) son[x]=v;
    	}
    }
    void dfs2(int x,int topf){
    	top[x]=topf; if(son[x]){
    		dfs2(son[x],topf);
    		for(int i=fst[x];i;i=e[i].nxt){
    			int v=e[i].to; if(v==fa[x]||v==son[x])continue;
    			dfs2(v,v);
    		}
    	}
    }
    int getlca(int x,int y){
    	for(;top[x]!=top[y];dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]]);
    	return dep[x]<dep[y]?x:y;
    }
    int getdist(int x,int y){
    	int lca=getlca(x,y);
    	return 1ll*dep[x]+1ll*dep[y]-2ll*dep[lca];
    }
    struct node{
    	int l,r,lca;
    }t[N<<2];
    inline void pushup(int x){
    	t[x].lca=getlca(t[x<<1].lca,t[x<<1|1].lca);
    }
    void build(int x,int l,int r){
    	t[x]=(node){l,r,0};
    	if(l==r){t[x].lca=l;return;} int mid=(l+r)>>1;
    	build(x<<1,l,mid),build(x<<1|1,mid+1,r);
    	pushup(x);
    }
    int qry(int x,int ql,int qr){
    	int l=t[x].l,r=t[x].r;
    	if(ql<=l&&r<=qr){return t[x].lca;}
    	int mid=(l+r)>>1;
    	if(qr<=mid) return qry(x<<1,ql,qr);
    	else if(mid<ql) return qry(x<<1|1,ql,qr);
    	else return getlca(qry(x<<1,ql,qr),qry(x<<1|1,ql,qr));
    }
    struct Segtree{
    	int lc[N*40],rc[N*40],sum[N*40],cnt;
    	void ins(int pre,int &x,int l,int r,int p,int v=1){
    		x=++cnt,lc[x]=lc[pre],rc[x]=rc[pre],sum[x]=sum[pre]+v;
    		if(l==r)return;	int mid=(l+r)>>1;
    		if(p<=mid) ins(lc[pre],lc[x],l,mid,p,v);
    		else ins(rc[pre],rc[x],mid+1,r,p,v);
    	}
    	void build(int &x,int l,int r){
    		x=++cnt,sum[x]=lc[x]=rc[x]=0; if(l==r)return;
    		int mid=(l+r)>>1; build(lc[x],l,mid),build(rc[x],mid+1,r);
    	}
    	int qry(int x,int y,int l,int r,int ql,int qr){
    		if(ql<=l&&r<=qr) return sum[y]-sum[x]; int mid=(l+r)>>1,ret=0;
    		if(ql<=mid) ret+=qry(lc[x],lc[y],l,mid,ql,qr);
    		if(mid<qr) ret+=qry(rc[x],rc[y],mid+1,r,ql,qr);
    		return ret;
    	}
    }T;
    int rt[N],n,Q;
    void init(){
    	dfs1(1,0,0,1),dfs2(1,1);
    	T.build(rt[0],1,n); build(1,1,n);
    	for(int i=1;i<=idx;i++) T.ins(rt[i-1],rt[i],1,n,id[i]);
    }
    int main(){
    	scanf("%d%d",&n,&Q);
    	for(int i=1,x,y,w;i<n;i++) scanf("%d%d%d",&x,&y,&w),addedge(x,y,w);
    	init();
    	int lastans=0; while(Q--){
    		int p,l,r; scanf("%d%d%d",&p,&l,&r);
    		p^=lastans,l^=lastans,r^=lastans;
    		int k1=T.qry(rt[dfn[p]-1],rt[dfn[p]+size[p]-1],1,n,l,r);
    		if(k1==r-l+1){printf("%d
    ",lastans=dep[qry(1,l,r)]-dep[p]);continue;}
    		if(k1!=0){lastans=0,puts("0");continue;}
    		int x=p; for(int i=18;i>=0;i--){
    			int nxt=anc[x][i]; if(!nxt) continue;
    			if(T.qry(rt[dfn[nxt]-1],rt[dfn[nxt]+size[nxt]-1],1,n,l,r)==0) x=nxt;
    		}
    		x=fa[x];
    		int lca=qry(1,l,r);
    		if(dis[lca]>dis[x]) printf("%d
    ",lastans=getdist(p,lca));
    		else printf("%d
    ",lastans=dep[p]-dep[x]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    厚积薄发,丰富的公用类库积累,助你高效进行系统开发(9)各种常用辅助类
    厚积薄发,丰富的公用类库积累,助你高效进行系统开发(7)声音播放、硬件信息、键盘模拟及钩子、鼠标模拟及钩子等设备相关
    厚积薄发,丰富的公用类库积累,助你高效进行系统开发(6)全屏截图、图标获取、图片打印、页面预览截屏、图片复杂操作等
    厚积薄发,丰富的公用类库积累,助你高效进行系统开发(4)CSV、Excel、INI文件、独立存储等文件相关
    DevExpress控件使用经验总结
    Winform开发框架之通用自动更新模块
    详解在数据查看界面中增加记录导航功能,你应该需要的
    使用Aspose.Cell控件实现多个Excel文件的合并
    厚积薄发,丰富的公用类库积累,助你高效进行系统开发(8)非对称加密、BASE64加密、MD5等常用加密处理
    WCF开发框架形成之旅如何实现X509证书加密
  • 原文地址:https://www.cnblogs.com/wxq1229/p/12294920.html
Copyright © 2020-2023  润新知