【栈模拟dfs】Cells UVALive

题目链接:https://cn.vjudge.net/contest/209473#problem/D

题目大意:有一棵树,这棵树的前n个节点拥有子节点,告诉你n的大小,以及这n个节点各有的子节点个数,对于Q个询问<x,y>,回答x是否是y的祖先。

解题思路:

一开始想的LCA,倍增解决。然后一看数据范围,2e7!瞬间被吓回来了。

想了各种方法百思不得其解最后看了题解orz(强烈建议独立思考,挫败感很强!)。

题解……万分机智,我们思考一下深搜建树的过程,先遍历到一个父亲节点,再往下遍历到这个父亲节点的子节点,再往上回溯到该父亲节点。我们发现如果A是B的祖先,A的遍历时间点早于B的遍历时间点早于B的回溯时间点早于A的回溯时间点。

也就是说我们如果给每个点建立两个时间值(类似Tarjan的思想,预计后天讲Tarjan),第一个时间值是遍历时间点,第二个时间值是回溯时间点,建树的时候预处理出来,那么对于每个询问,就可以O(1)得出结果了(判断一下四个值大小关系)。

这样乍一听好像很好实现,然而此题还有一个trick,就是什么样的电脑才能深搜递归2e7层?果断会爆栈!

最后这道题的难点(?)转化成了手动扩栈模拟dfs,很简单的样子,就不赘述了。

坑点:注意数组大小,容易RE(别开太大了,稍微大一点就好)。

下面放965msAC代码:

 1 /* by Lstg */
 2 /* 2018-03-05 21:02:03 */
 3 
 4 #include<stdio.h>
 5 #include<iostream>
 6 #include<queue>
 7 #define MAXN 20000005
 8 using namespace std;
 9 
10 int dfn[MAXN],dfm[MAXN],stk[MAXN],n;
11 queue<int>g[300005];//这里太大就会RE
12 
13 void _mydfs(int x){
14     
15     int top=0,num=1,y;
16     dfn[x]=num++;
17     stk[++top]=x;
18     while(top){
19         x=stk[top];
20         if(x>n||g[x].empty()){/*先判断x的大小防RE和WA(有时溢出不会告诉你是RE,而会返回WA)*/
21             dfm[x]=num++;
22             top--;
23         }
24         else{
25             y=g[x].front();
26             g[x].pop();
27             dfn[y]=num++;
28             stk[++top]=y;
29         }
30     }
31 }
32 
33 int main(){
34 
35     int T,tot,i,x,y;
36     scanf("%d",&T);
37     for(int cc=1;cc<=T;cc++){
38         scanf("%d",&n);
39         tot=0;
40         
41         for(i=0;i<n;i++){
42             while(!g[i].empty())g[i].pop();
43             scanf("%d",&x);
44             while(x--)
45                 g[i].push(++tot);
46         }
47         _mydfs(0);
48         printf("Case %d:
",cc);
49         scanf("%d",&n);
50         for(i=1;i<=n;i++){
51             scanf("%d%d",&x,&y);
52             if(dfn[x]<dfn[y]&&dfm[x]>dfm[y])
53                 puts("Yes");
54             else puts("No");
55         }
56         if(cc!=T)putchar(10);
57     }
58     return 0;
59 }
原文地址:https://www.cnblogs.com/L-Excalibur/p/8511527.html