1. 程式人生 > >[bzoj4281][ONTAK2015]Związek Harcerstwa Bajtockiego_倍增LCA

[bzoj4281][ONTAK2015]Związek Harcerstwa Bajtockiego_倍增LCA

Związek Harcerstwa Bajtockiego bzoj-4281 ONTAK-2015

題目大意:給定一棵有n個點的無根樹,相鄰的點之間的距離為1,一開始你位於m點。之後你將依次收到k個指令,每個指令包含兩個整數d和t,你需要沿著最短路在t步之內(包含t步)走到d點,如果不能走到,則停在最後到達的那個點。請在每個指令之後輸出你所在的位置。

註釋:$1\le n,m,k\le 10^6$,$m\le n$。


想法:

對於每一個指令,用倍增LCA判一下能不能到達即可。

Code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 1000010 
using namespace std;
int f[22][N],dep[N];
int head[N],to[N<<1],nxt[N<<1],tot;
inline char nc() {static char *p1,*p2,buf[100000]; return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;}
int rd() {int x=0; char c=nc(); while(!isdigit(c)) c=nc(); while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=nc(); return x;}
inline void add(int x,int y) {to[++tot]=y; nxt[tot]=head[x]; head[x]=tot;}
void dfs(int pos,int fa)
{
	dep[pos]=dep[fa]+1;
	f[0][pos]=fa; for(int i=1;i<=20;i++) f[i][pos]=f[i-1][f[i-1][pos]];
	for(int i=head[pos];i;i=nxt[i]) if(to[i]!=fa) dfs(to[i],pos);
}
int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=20;~i;i--) if(dep[f[i][x]]>=dep[y]) x=f[i][x];
	if(x==y) return x;
	for(int i=20;~i;i--) if(f[i][x]!=f[i][y]) x=f[i][x],y=f[i][y];
	return f[0][x];
}
int clm(int x,int d)
{
	for(int i=20;~i;i--) if((1<<i)<=d) x=f[i][x],d-=(1<<i);
	return x;
}
int main()
{
	int n=rd(),pre=rd(),k=rd();
	for(int i=1;i<n;i++) {int x=rd(),y=rd(); add(x,y); add(y,x);}
	dfs(1,1);
	for(int i=1;i<=k;i++)
	{
		int x=rd(),y=rd(),z=lca(x,pre);
		if(dep[pre]-dep[z]>=y) pre=clm(pre,y);
		else if(dep[x]+dep[pre]-2*dep[z]<=y) pre=x;
		else y=dep[x]+dep[pre]-2*dep[z]-y,pre=clm(x,y);
		printf("%d ",pre);
	}
	puts("");
	return 0;
}
/*
3 1 2

1 2

2 3

3 4

1 1
*/

小結:水題啊。