1. 程式人生 > >2018.11.01 NOIP訓練 圖論(線段樹+倍增+dfs序)

2018.11.01 NOIP訓練 圖論(線段樹+倍增+dfs序)

傳送門 一道挺妙的題。

對於詢問點(u,v),如右圖所示,我們可以發現存在一個點m在u->v的路徑中,m子樹的點到u是最近的,m子樹外到v是最近的。其中dis(u,m)=(dis(u,v)-1)/2,且deep[u]>deep[v] 在這裡插入圖片描述 根據這個結論,問題轉換為m子樹中找出距離u最大的點,在m子樹外找出距離v的最大的點。 子樹的資訊維護最大值自然可以想到dfs序+線段樹。 維護的演算法步驟:

  1. 求出每個點到根節點的距離dis[i]
  2. 對所有的詢問離線成2個數組ans1,ans2,ans1記錄詢問點對中深度大的點+mid值,ans2記錄詢問點對中深度小的點+mid值
  3. 對dfs樹進行一次dfs,向下走一步,對於每個點,子樹內的點距離當前子樹根的距離-1,子樹外的點距離當前子樹根的距離+1
  4. 統計每個點作為詢問點的答案
  5. 對於每組詢問取ans1[i],ans2[i]較大的。

然後碼一碼就能過了。 其實昨天寫的那個線上做法也能做,但細節比較多。 程式碼:

#include<bits/stdc++.h>
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (T[p].l+T[p].r>>1)
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar
(); while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar(); return ans; } const int N=1e6+5,inf=-0x3f3f3f3f; int n,m,first[N],in[N],out[N],dep[N],tot=0,cnt=0,pred[N],st[N][21],ans[N][2],p1=1,p2=1; struct edge{int v,next;}e[N<<1]; struct Node{int l,r,mx,lz;}T[N<<2]; struct
Qu{ int rt,pos,id; friend inline bool operator<(const Qu&a,const Qu&b){return in[a.rt]==in[b.rt]?in[a.pos]<in[b.pos]:in[a.rt]<in[b.rt];} }q1[N],q2[N]; inline void add(int u,int v){e[++cnt].v=v,e[cnt].next=first[u],first[u]=cnt;} inline void dfs1(int p){ pred[in[p]=++tot]=p; for(int i=1;i<=20;++i)st[p][i]=st[st[p][i-1]][i-1]; for(int i=first[p];i;i=e[i].next){ int v=e[i].v; if(v==st[p][0])continue; dep[v]=dep[p]+1,st[v][0]=p,dfs1(v); } out[p]=tot; } inline int lca(int x,int y){ if(dep[x]<dep[y])swap(x,y); int tmp=dep[x]-dep[y]; for(int i=20;~i;--i)if((tmp>>i)&1)x=st[x][i]; if(x==y)return x; for(int i=20;~i;--i)if(st[x][i]^st[y][i])x=st[x][i],y=st[y][i]; return st[x][0]; } inline int find(int p,int len){ for(int i=20;~i;--i)if((len>>i)&1)p=st[p][i]; return p; } inline void pushup(int p){T[p].mx=max(T[lc].mx,T[rc].mx);} inline void pushnow(int p,int v){T[p].lz+=v,T[p].mx+=v;} inline void pushdown(int p){if(T[p].lz)pushnow(lc,T[p].lz),pushnow(rc,T[p].lz),T[p].lz=0;} inline void build(int p,int l,int r){ T[p].l=l,T[p].r=r; if(T[p].l==T[p].r){T[p].mx=dep[pred[l]];return;} build(lc,l,mid),build(rc,mid+1,r),pushup(p); } inline void update(int p,int ql,int qr,int v){ if(ql>T[p].r||qr<T[p].l)return; if(ql<=T[p].l&&T[p].r<=qr)return pushnow(p,v); pushdown(p); if(qr<=mid)update(lc,ql,qr,v); else if(ql>mid)update(rc,ql,qr,v); else update(lc,ql,mid,v),update(rc,mid+1,qr,v); pushup(p); } inline int query(int p,int ql,int qr){ if(ql>T[p].r||qr<T[p].l)return inf; if(ql<=T[p].l&&T[p].r<=qr)return T[p].mx; pushdown(p); if(qr<=mid)return query(lc,ql,qr); if(ql>mid)return query(rc,ql,qr); return max(query(lc,ql,mid),query(rc,mid+1,qr)); } inline void dfs2(int p){ while(p1<=m&&q1[p1].rt==p)ans[q1[p1].id][0]=query(1,in[q1[p1].pos],out[q1[p1].pos]),++p1; while(p2<=m&&q2[p2].rt==p)ans[q2[p2].id][1]=max(query(1,1,in[q2[p2].pos]-1),query(1,out[q2[p2].pos]+1,n)),++p2; for(int i=first[p];i;i=e[i].next){ int v=e[i].v; if(v==st[p][0])continue; update(1,in[v],out[v],-1),update(1,1,in[v]-1,1),update(1,out[v]+1,n,1); dfs2(v); update(1,in[v],out[v],1),update(1,1,in[v]-1,-1),update(1,out[v]+1,n,-1); } } int main(){ freopen("lx.in","r",stdin); n=read(); for(int i=1,u,v;i<n;++i)u=read(),v=read(),add(u,v),add(v,u); dfs1(1),build(1,1,n),m=read(); for(int i=1,d,x,y,t,md;i<=m;++i){ x=read(),y=read(),t=lca(x,y),d=dep[x]+dep[y]-2*dep[t]; if(dep[x]<dep[y])swap(x,y); md=find(x,(d-1)/2), q1[i]=(Qu){x,md,i},q2[i]=(Qu){y,md,i}; } sort(q1+1,q1+m+1),sort(q2+1,q2+m+1),dfs2(1); for(int i=1;i<=m;++i)printf("%d\n",max(ans[i][0],ans[i][1])); return 0; }