题目描述:
给一颗结点数为(100,000)的树,最多询问100,000次。每次询问对两个结点X,Y,以X为根,Y的最小标号的孩子,Y的最小标号的后代。
吐槽:
1. 这么难写的题为什么大家都当水题做阿...
算法分析:
对于X,Y,我们求出其LCA,U。
如果U是Y的父亲,那么一切都不变。用预处理的结果就可以。
如果U是Y,那么我们可以快速求出,含有后代X的Y的儿子X'。
检查X'是否是Y的最小孩子(儿子)。如果是,那么结果是Y的次小儿子(孩子)。否则是Y的最小儿子(孩子)。
于是我们树形DP,求出最小与次小(儿子,后代)就可以了。写起来很猥琐,容易出错。
LCA的写法是参照shi哥的代码。
先预处理求出u的(1,2,4,8,...)代祖先,
先让x,y处于同一层。
然后让x,y一点一点向上爬,直到爬到恰好x==y的位置就是LCA了。
#include<iostream>
#include<cassert>
#include<cstdio>
using namespace std;
const int maxb = 17;
const int V = 1<<maxb;
int e,P[V][maxb], head[V], nxt[V<<1], pnt[V<<1], mn[V], sd[V], mn1[V],sd1[V];
// dp
const int inf = ~0u>>2;
int dep[V];
void dfs(int u,int f){
P[u][0] = f;
for(int i =1; i< maxb; i++)
P[u][i] = P[P[u][i-1]][i-1];
if(f!=u) mn[u] = f; else mn[u] = inf;
mn1[u] = sd1[u] = inf;
sd[u] = inf;
for(int i=head[u];i!=-1;i=nxt[i]) {
int v = pnt[i];
if(v == f) continue;
dep[v] = dep[u] + 1;
if(v < mn[u]) {sd[u] = mn[u]; mn[u] = v;}
else if(v < sd[u]) sd[u] = v;
dfs(v,u);
int fst,snd;
if(v > mn1[v]) {
fst = mn1[v];
snd = min(v, sd1[v]);
}
else {
fst = v;
snd = mn1[v];
}
if(fst < mn1[u]){
sd1[u] = mn1[u];
mn1[u] = fst;
}
else {
sd1[u] = min(sd1[u],fst);
}
}
}
// build
void add(int u,int v){
nxt[e] = head[u];
head[u] = e;
pnt[e++] = v;
}
// LCA
void go(int &u,int d){
for(int i=0;i<maxb;i++){
if((1<<i) & d) u = P[u][i];
}
}
int LCA(int x,int y){
if(dep[x] > dep[y]) swap(x,y);
go(y,dep[y] - dep[x]);
if(x == y) return x;
for(int i=maxb-1;i>=0;i--){
if(P[x][i] != P[y][i])
x = P[x][i], y = P[y][i];
}
assert(P[x][0]==P[y][0]);
return P[x][0];
}
// work
void work(int x,int y){
int u = LCA(x,y);
if(sd[y] == inf) {puts("no answers!"); return;}
if(u!=y){
printf("%d %d\n",P[y][0] == mn[y] ? sd[y] : mn[y], mn1[y]);
}
else {
go(x,dep[x]-dep[y]-1);
printf("%d %d\n",x == mn[y] ? sd[y]: mn[y] , y == 1 ? (min(mn1[x],x) == mn1[y]? sd1[y]: mn1[y]): 1);
}
}
int main(){
int test;
scanf("%d",&test);
while(test--){
int u,v,n,m;
e = 0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) head[i] = -1;
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
dep[1] = 0;
dfs(1,1);
while(m--){
scanf("%d%d",&u,&v);
work(u,v);
}
puts("");
}
}
posted on 2012-07-17 10:53
西月弦 阅读(493)
评论(0) 编辑 收藏 引用 所属分类:
解题报告