• 【栈模拟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 }
  • 相关阅读:
    顺时针打印矩阵
    topK问题
    9. Palindrome Number(回文数)
    Spinner用法详解
    翻转字符串
    清雨的自助餐(斐波那契数列的应用)
    2. Add Two Numbers(链表尾插法)
    assign和weak的区别
    14-最长公共前缀
    12 13-int与罗马数字转换
  • 原文地址:https://www.cnblogs.com/L-Excalibur/p/8511527.html
Copyright © 2020-2023  润新知