祖孙询问 (模拟栈代替DFS)

祖孙询问

(tree.pas/c/cpp)

【问题描述】

    已知一棵n个节点的有根树。有m个询问。每个询问给出了一对节点的编号x和y,询问x与y的祖孙关系。

【输入格式】

    输入第一行包括一个整数n表示节点个数。

    接下来n行每行一对整数对a和b表示a和b之间有连边。如果b是-1,那么a就是树的根。

    第n+2行是一个整数m表示询问个数。

    接下来m行,每行两个正整数x和y。

【输出格式】

    对于每一个询问,输出1:如果x是y的祖先,输出2:如果y是x的祖先,否则输出0。

【样例输入】

10

234 -1

12 234

13 234

14 234

15 234

16 234

17 234

18 234

19 234

233 19

5

234233

23312

23313

23315

23319

【样例输出】

1

0

0

0

2

【数据规模】

    对于30%的数据,n,m≤1000。

    对于100%的.据,n,m≤40000,每个节点的编号都不超过40000。

先看看标准方法求LCA(msm法):

为了处理爆栈而进行各种压缩变量,卡内存过关(边表记得*2)

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<functional>
#include<algorithm>
#include<iostream>
using namespace std;
#define MAXN (80000+10)
/*
#define f( i , j ) f[ ( i ) + 2 , j ]
#define b( i ) b [ ( i ) + 2  ]
*/
int n,m,f[MAXN][100],len[MAXN],deep[MAXN];
int head[MAXN],next[MAXN],edge[MAXN],siz=0;
int log2(int x)
{
    return (int)trunc(log(x)/log(2));
}
void addedge(int u,int v)
{
     edge[++siz]=v;
     next[siz]=head[u];
     head[u]=siz;
}
int a[MAXN]={0},tot=0;
bool b[MAXN];
void dfs(int x,int dep)
{
     /*
     cout<<x<<':';
    for (int i=1;i<=tot;i++) cout<<a[i]<<' ';
    cout<<endl;
    */
    b[x]=1;
    int p=head[x];
    while (p)
    {
         int now=edge[p];
         tot++;
         a[tot]=now;
         if (!b[now]) dfs(now,dep+1);
         tot--;
         p=next[p];
    }
    /*
     cout<<x<<':';
    for (int i=1;i<=tot;i++) cout<<a[i]<<' ';
    cout<<endl;
    */
    int j=0;
    for (int i=tot-1;i>=1;i-=(tot-i))
    {
        f[x][j]=a[i];
        j++;
    }
    len[x]=j-1;
    deep[x]=dep;
}
int father(int x,int y)
{
    while (y)
    {
          int p=log2(y);
          x=f[x][p];
          y-=(1<<p);
    }
    return x;
}
int main()
{
    freopen("tree.in","r",stdin);
  freopen("tree.out","w",stdout);

    memset(head,0,sizeof(head));
    memset(next,0,sizeof(next));
    memset(edge,0,sizeof(edge));
    memset(len,0,sizeof(len));
    memset(b,0,sizeof(b));
    memset(deep,0,sizeof(deep));
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        addedge(x+2,y+2);addedge(y+2,x+2);
    }
    a[1]=1;tot=1;
    dfs(1,0);
    /*
    for (int i=1;i<=n+2;i++)
    {
        cout<<i<<' '<<len[i]<<':';
        for (int j=0;j<=len[i];j++) cout<<f[i][j]<<' ';
        cout<<endl;

    }
    */




    scanf("%d",&m);



    for (int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        x+=2;y+=2;
      //  cout<<x<<' '<<y<<' '<<deep[x]<<' '<<deep[y]<<endl;
        if (x==1) printf("1n");
        else if (y==1) printf("2n");
        else
        {
            int depx=deep[x],depy=deep[y];
            if (depx<depy)
            {
               if (father(y,depy-depx)==x) printf("1n");
               else printf("0n");
            }
            else if (depx>depy)
            {
                 if (father(x,depx-depy)==y) printf("2n");
                 else printf("0n");
            }
            else printf("0n");
        }

    }

  //  while (1);


    return 0;
}

考虑欧拉路径——它可以判断2个节点的从属情况

模拟栈版本:巧用while循环代替dfs

Program tree;
const
   maxn=40000;
   maxm=40000;
var
   n,m,i,j,u,v,size,x,y:longint;
   head,next,edge:array[-1..maxm*2] of longint;
// dfs
   stack:array[1..maxm*10] of longint;
   b:array[-1..maxn] of boolean;
   l,r,father:array[-1..maxn] of longint;
   time,nowstack:longint;
Procedure addedge(u,v:longint);
begin
   inc(size);
   edge[size]:=v;
   next[size]:=head[u];
   head[u]:=size;
end;
Procedure dfs;
var
   i,j,p,now,v:longint;
   flag:boolean;
begin
   time:=0;nowstack:=1;
   stack[1]:=-1;
   while (nowstack>0) do
   begin
      flag:=false;
      now:=stack[nowstack];
      inc(time);

      if not(b[now]) then begin b[now]:=true;l[now]:=time; end;



      p:=head[now];
      while (p>0) do
      begin
         if not(b[edge[p]]) then
         begin
            v:=edge[p];
            father[v]:=now;
            inc(nowstack);
            stack[nowstack]:=v;
            flag:=true;break;
         end;
         p:=next[p];
      end;
      if flag then continue;
      inc(time);  r[now]:=time; dec(nowstack);
   end;
end;
begin
   assign(input,'tree.in');
   assign(output,'tree.out');
   reset(input);
   rewrite(output);
   read(n);
   fillchar(head,sizeof(head),0);
   fillchar(l,sizeof(l),0);
   fillchar(r,sizeof(r),0);
   fillchar(b,sizeof(b),false);
   size:=0;
   for i:=1 to n do
   begin
      read(u,v);
      addedge(u,v); addedge(v,u);
   end;

   dfs;

   read(m);
   for i:=1 to m do
   begin
      read(x,y);
      if (l[x]<l[y]) and (r[y]<r[x]) then writeln('1')
      else if (l[y]<l[x]) and (r[x]<r[y]) then writeln('2')
      else writeln('0');
   end;

   close(input);
   close(output);
end.

POJ 1363(栈)

此题纯模拟也能过……

   我更懒,直接记栈尾元素和已出元素……

Program P1363;
var
   n,i:longint;
   a:array[1..1000] of longint;
   b:array[0..1000] of boolean;
   t:longint;
   tag:boolean;
begin
   read(n);
   while (n>0) do
   begin
      read(a[1]);
      while (a[1]>0) do
      begin
         fillchar(b,sizeof(b),false);
         for i:=2 to n do read(a[i]);
         t:=a[1];
         b[t]:=true;
         dec(t);
         tag:=false;
         for i:=2 to n do
         begin
            if not(b[a[i]]) then
            begin
               if a[i]>t then
               begin
                  t:=a[i];
                  b[t]:=true;
                  dec(t);
                  while b[t] do dec(t);
               end
               else if a[i]=t then
               begin
                  b[t]:=true;
                  dec(t);
                  while b[t] do dec(t);
               end
               else
               begin
                  writeln('No');
                  tag:=true;
                  break;
               end;
            end
            else
            begin
               writeln('No');
               tag:=true;
               break;
            end;
         end;
         if not(tag) then writeln('Yes');

         read(a[1]);
      end;
      writeln;
      read(n);
   end;
end.