2018.11.01【NOIP训练】图论(树上倍增)(线段树)(DFS序)(离线处理)

传送门


解析:

有一道差不多。但是我用了在线算法做的题:链接

这里用一种好想的多的离线算法来做这道题。

思路:

其实还是断边,将一个询问变成两个来做显然需要断的是两个点路径中点。

然后将询问分为两类处理,root=u,ban=vroot=u,ban=vroot=u,ban=vuuuvvv的子树中或不在vvv的子树中,其实就算v==lcav==lcav==lca,我们还是可以这样做,但是需要修改一点,因为lcalcalca以上的部分到两者的距离是相等的,会影响答案,所以我们随便偏一点就好了。

uuuvvv的子树中,则uuu无法从子树中出去,最远点就在vvv的子树中询问。
uuuvvv的子树中,则uuu无法进入子树,最远点就在vvv的子树以外的地方询问。

然后预处理一下DFSDFSDFS序,发现向相邻节点走一步实际上就是改变了其他节点到当前位置的距离,子树内的距离-1,其他地方距离+1。

所以我们维护一颗支持区间加,区间询问最大值的线段树就好了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

cs int N=1000006,logN=20;
int last[N],nxt[N<<1],to[N<<1],ecnt; 
inline void addedge(int u,int v){
	nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v;
	nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u;
}

int fa[N][logN+1],dep[N],in[N],out[N],pos[N],tot;
inline void dfs1(int u){
	pos[in[u]=++tot]=u;
	for(int re i=1;i<=logN;++i)fa[u][i]=fa[fa[u][i-1]][i-1];
	for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
		if(v==fa[u][0])continue;
		fa[v][0]=u;
		dep[v]=dep[u]+1;
		dfs1(v);	
	}
	out[u]=tot;
}

inline int jump(int u,int step){
	for(int re i=logN;~i;--i){
		if((1<<i)<=step){
			step-=1<<i;
			u=fa[u][i];
		}
	}
	return u;
}

inline int LCA(int u,int v){
	if(dep[u]>dep[v])swap(u,v);
	if(dep[u]<dep[v])v=jump(v,dep[v]-dep[u]);
	if(v==u)return u;
	for(int re i=logN;~i;--i){
		if(fa[u][i]!=fa[v][i]){
			u=fa[u][i];
			v=fa[v][i];
		}
	}
	return fa[u][0];
}

int add[N<<2],maxn[N<<2];
inline void pushnow(int k,int val){maxn[k]+=val,add[k]+=val;}
inline void pushdown(int k){if(add[k]){pushnow(k<<1,add[k]);pushnow(k<<1|1,add[k]);add[k]=0;}}
inline void pushup(int k){maxn[k]=max(maxn[k<<1],maxn[k<<1|1]);}

inline void build(int k,int l,int r){
	if(l==r){
		maxn[k]=dep[pos[l]];
		return ;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	pushup(k);
} 

inline void modify(int k,int l,int r,cs int &ql,cs int &qr,cs int &val){
	if(ql>qr)return ;
	if(ql<=l&&r<=qr)return pushnow(k,val);
	pushdown(k);
	int mid=(l+r)>>1;
	if(ql<=mid)modify(k<<1,l,mid,ql,qr,val);
	if(qr>mid)modify(k<<1|1,mid+1,r,ql,qr,val);
	pushup(k);
}

inline int query(int k,int l,int r,cs int &ql,cs int &qr){
	if(ql>qr)return -0x3f3f3f3f;
	if(ql<=l&&r<=qr)return maxn[k];
	pushdown(k);
	int mid=(l+r)>>1;
	if(qr<=mid)return query(k<<1,l,mid,ql,qr);
	if(ql>mid)return query(k<<1|1,mid+1,r,ql,qr);
	return max(query(k<<1,l,mid,ql,qr),query(k<<1|1,mid+1,r,ql,qr));
}

int n,m;
struct Query{
	int root,pos,id;
	friend bool operator<(cs Query &a,cs Query &b){
		return in[a.root]<in[b.root];
	}
}q1[N],q2[N]; 
int ans1[N],ans2[N],h1=1,h2=1;

inline void dfs2(int u){
	while(h1<=m&&q1[h1].root==u)
	ans1[q1[h1].id]=query(1,1,n,in[q1[h1].pos],out[q1[h1].pos]),++h1;
	while(h2<=m&&q2[h2].root==u)
	ans2[q2[h2].id]=max(query(1,1,n,1,in[q2[h2].pos]-1),query(1,1,n,out[q2[h2].pos]+1,n)),++h2;
	for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
		if(v==fa[u][0])continue;
		modify(1,1,n,in[v],out[v],-1);
		modify(1,1,n,1,in[v]-1,1);
		modify(1,1,n,out[v]+1,n,1);
		dfs2(v);
		modify(1,1,n,in[v],out[v],1);
		modify(1,1,n,1,in[v]-1,-1);
		modify(1,1,n,out[v]+1,n,-1);
	}
}

signed main(){
	n=getint();
	for(int re i=1;i<n;++i){
		int u=getint(),v=getint();
		addedge(u,v);
	}
	fa[1][0]=1;
	dfs1(1);
	build(1,1,n);
	m=getint();
	for(int re i=1;i<=m;++i){
		int u=getint(),v=getint();
		if(dep[u]>dep[v])swap(u,v);
		int lca=LCA(u,v),dist=dep[u]+dep[v]-2*dep[lca];
		int pos=jump(v,(dist-1)/2);
		q1[i]=(Query){v,pos,i};
		q2[i]=(Query){u,pos,i};
	}
	sort(q1+1,q1+m+1);
	sort(q2+1,q2+m+1);
	dfs2(1);
	for(int re i=1;i<=m;++i)printf("%d\n",max(ans1[i],ans2[i]));
	return 0;
}

转载于:https://www.cnblogs.com/zxyoi/p/10047127.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值