?? 2615 dfs(非遞歸).cpp
字號:
//zoj 2615 搜索(非遞歸)
/*
給出一顆最多包含三千萬個點的樹,需要處理一百萬次查詢:
給定a,b,詢問a是否b的祖先。
解決思路:
查詢的數量很大,必須在O(1)時間內回答每一次查詢。參考LCA轉化RMQ過程。
建立樹,在遍歷過程中記錄所有第一次到達節點i的時間in[i],離開節點i的時間out[i]。然后看區間[ in[b], out[b] ]是否被 [in[a], out[a]]所真包含。若包含,說明a是b的祖先。
由于需要建二叉樹,規模小的情況可以定義樹節點類型,需要對樹遍歷時遞歸進行。
可是一旦規模變大,例如本題中的三千萬個節點,在時間、空間限制下,上述方法不再可行。如果遞歸遍歷,很可能導致堆棧溢出。
完全二叉樹通常使用數組實現。但這里的樹是多叉,并且不一定是完全的:有一些中間節點可能沒有兒子。但仍然可以借鑒使用數組描述樹的思想。定義如下數據結構:
*/
#include "stdio.h"
#define maksn 300100
#define maks 19000100
int begin[maksn]; //index of i's first child
int cnt[maksn]; //the number of i's children
int in[maks]; //the time of first visit node i
int out[maksn]; //the time leave node i
int p[maks]; //parent index of node i. WA when p[maksn]
int n;
void maketree()
{
int len=1,i,k;
scanf("%d",&n);
for(i=0;i<n;++i)
{
scanf("%d",&cnt[i]);
begin[i]=len;
len+=cnt[i];
for(k=0;k<cnt[i];++k)
p[begin[i] + k]=i;
}
}
void dfs()
{
int push=1, pop=0;
int oper=push, node=0,time=0;
while(1)
{
if(oper==push)//to visit the tree rooted at node
{
in[node]=time++;
if(cnt[node])//node has children
{
if(begin[node]<n)//first child of node within the boundary
node=begin[node];//dfs the subtree
else//all children of node exceeds the boundary
{
for(int i=0;i<cnt[node];++i)
{
in[begin[node]+i]=time;
time+=2;//out[begin[node]+i] = in[begin[node]+i]+1;
}
oper=pop;//already visited the leaves
}
}
else//node is leave
oper=pop;
}
else//have visited the tree rooted at node
{
out[node]=time++;//leave node
if(node==0)
break;
int parent=p[node];
if(node<begin[parent]+cnt[parent]-1)
{ //node has siblings.
++node;//move to sibling
oper=push;
if(node>=n)
{//boundary case:
//node is within boundary while siblings not.
//i.e, the boundary splits the children of parent of node
//the siblings must be leaves
for(;node<begin[parent]+cnt[parent];++node,time+=2)
in[node]=time;
node=parent;//visited all children.
oper=pop;//backtrack to parent
}
}
else//node has no siblings
node=parent;
}
}
}
int main()
{
int t,i,j,m,kase=1,a,b;
//freopen("2615.txt","r",stdin);
scanf("%d",&t);
while(t--)
{
maketree();
dfs();
scanf("%d",&m);
printf("Case %d:\n",kase++);
for(i=0;i<m;i++)
{
scanf("%d %d",&a,&b);
if(a>=n||a==b)
{
puts("No");
continue;
}
int leave=(b<n? out[b]:in[b]+1);
if(in[b]>in[a] && leave<out[a])
puts("Yes");
else
puts("No");
}
if(t) printf("\n");
}
return 0;
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -