标签:
题意: 一颗结点数为(100,000)的树,最多询问100,000次。每次询问对两个结点X,Y,以X为根,Y的最小标号的孩子,Y的最小标号的后代。
思路:如果dfs n次那么时间复杂度无法承受,我们考虑只dfs一次。
以1为根节点dfs一次,记录每个点的最小和次小儿子值和最小后代值,考虑询问x, y,如果x不是y的子孙节点,那么答案就是y的最小儿子值和最小后代值。
如果x是y的子孙节点,又分为两种情况,
首先考虑y不为1的时候,那么y的最小后代值一定为1,如果x所在子树的y的儿子节点的值为最小儿子值,那么输出 次小儿子值和y父亲结点值中的较小者为 要求输出的y的最小儿子值。
其次考虑y为1的情况,求最小儿子值和上一步类似,现在我们来求最小后代值,
先要预处理出1的每个儿子的最小后代值(包括自身),保留其中最小的两个记为min0,min1,若x所在子树的y的儿子节点的最小后代值(包括自身)等于min0那么输出min1,
否则输出min0。
细节实现:
第一步:以1为根结点dfs一次,dfs的过程中记录每个点的最小和次小儿子值和最小后代值以及每个节点的父亲。
第二步:解决判断x是否是y子孙的问题,这一步可以利用时间戳,根据递归的思想,如果x的开始访问时间大于y且x的结束访问时间小于y,那么x一定是y的子孙。
第三步,利用倍增的思想,求x所在子树的y的儿子节点即xy路径上的最靠近y的那一点,方法是利用fa[i][j]表示i的第2^j个祖先,
那么可以递推出fa[i][j] = fa[ fa[i][j-1] ][ j-1 ]
ps:第一次写出这么优美的dfs一发就ac简直要感动哭了(开个玩笑^_^)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#define eps 1e-6
#define LL long long
#define pii (pair<int, int>)
using namespace std;
const int maxn = 100000 + 100;
const int INF = 0x3f3f3f3f;
int clock_cnt, n, q;
int tim[maxn][2], fa[maxn][20], minson[maxn][2], mindes[maxn];
vector<int> G[maxn];
void dfs(int u) {
tim[u][0] = ++clock_cnt;
minson[u][0] = minson[u][1] = mindes[u] = INF;
int sz = G[u].size();
for(int i = 0; i < sz; i++) {
int v = G[u][i];
if(tim[v][0]) continue;
if(v < minson[u][0]) { minson[u][1] = minson[u][0]; minson[u][0] = v; mindes[u] = min(mindes[u], v); }
else if(v < minson[u][1]) minson[u][1] = v;
dfs(v);
fa[v][0] = u;
mindes[u] = min(mindes[u], mindes[v]);
}
tim[u][1] = ++clock_cnt;
}
void init_fa() {
for(int i = 1; i < 17; i++) {
for(int j = 1; j <= n; j++) fa[j][i] = fa[fa[j][i-1]][i-1];
}
}
int find_son(int u, int v) {
if(fa[v][0] == u) return v;
for(int i = 16; i >= 0; i--) {
int t = fa[v][i];
if(fa[t][0] == u) return t;
else if(tim[t][0] > tim[u][0]) v = t;
}
}
int min1_0, min1_1;
void init_1() {
min1_0 = min1_1 = INF;
int sz = G[1].size();
for(int i = 0; i < sz; i++) {
int v = G[1][i];
int t = min(v, mindes[v]);
if(t < min1_0) { min1_1 = min1_0; min1_0 = t; }
else if(t < min1_1) min1_1 = t;
}
}
void solve(int x, int y) {
if(tim[x][0]>tim[y][0] && tim[x][1]<tim[y][1]) {
if(y != 1) {
int t = find_son(y, x);
int mson = (t == minson[y][0] ? minson[y][1] : minson[y][0]);
mson = min(mson, fa[y][0]);
printf("%d 1\n", mson);
}
else {
int t = find_son(y, x);
int mson = (t == minson[y][0] ? minson[y][1] : minson[y][0]);
if(mson == INF) puts("no answers!");
else {
int mintmp = min(t, mindes[t]);
int mdes = (mintmp == min1_0 ? min1_1 : min1_0);
printf("%d %d\n", mson, mdes);
}
}
}
else {
if(mindes[y] != INF) printf("%d %d\n", minson[y][0], mindes[y]);
else puts("no answers!");
}
}
void init() {
memset(tim, 0, sizeof(tim));
memset(fa, 0, sizeof(fa));
clock_cnt = 0;
for(int i = 1; i <= n; i++) {
G[i].clear();
minson[i][0] = minson[i][1] = mindes[i] = INF;
}
}
int main() {
// freopen("input.txt", "r", stdin);
int T; cin >> T;
while(T--) {
cin >> n >> q;
init();
for(int i = 0; i < n-1; i++) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1);
init_fa();
init_1();
// cout << tim[3][0] << " " << tim[3][1] << endl << tim[2][0] << " " << tim[2][1] << endl;
for(int i = 0; i < q; i++) {
int x, y;
scanf("%d%d", &x, &y);
solve(x, y);
}
puts("");
}
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
HDU 4008 Parent and son (数据结构)
标签:
原文地址:http://blog.csdn.net/u014664226/article/details/47269211