[noip模拟]祖孙询问<LCA>

【问题描述】

 

    已知一棵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

234 233

233 12

233 13

233 15

233 19

【样例输出】

1

0

0

0

2

【数据规模】

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

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

 

 

这是去年我们考的一套题,现在回过头来看,其实不难啊,谁知道当年还做的要死要活的

这题就是一个裸的LCA,毕竟题目都这样说了

我用了tarjan,因为个人来说我的tarjan打的要差一些,但是打了这题后,我对tarjan求LCA的理解要正常的许多了

裸的tarjan,额外处理就是储存一个对每一个询问边的最近公共祖先,但是我们建立询问边是建立了两条,所以要注意在储存答案时要合并起来

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<cstdlib>
 6 #include<queue>
 7 #include<cmath>
 8 #define maxn 40005
 9 using namespace std;
10 
11 struct node{
12     int u,v,nxt;
13 }e[maxn*4],p[maxn*4];
14 
15 int n,m,dfn[maxn],fa[maxn],belong[maxn],root,maxid;
16 int head[maxn],heap[maxn],vis[maxn*4],visid[maxn];
17 int ord[maxn*4];
18 
19 int tot;
20 void adde(int u,int v){e[tot]=(node){u,v,head[u]};head[u]=tot++;}
21 
22 int tpt;
23 void addp(int u,int v,int pos){p[tpt]=(node){u,v,heap[u]};
24 ord[tpt]=pos;heap[u]=tpt++;
25 }
26 
27 int find(int x){if(fa[x]==x)return x;return fa[x]=find(fa[x]);}
28 
29 int num;
30 void tarjan(int u,int from){
31     dfn[u]=++num;
32     for(int i=head[u];i!=-1;i=e[i].nxt ){
33         int v=e[i].v;
34         if(!dfn[v]){tarjan(v,u);fa[v]=u;visid[v]=1;}
35     }
36     for(int i=heap[u];i!=-1;i=p[i].nxt){
37         int vv=p[i].v;
38         if(visid[vv]&&!vis[ord[i]]){vis[ord[i]]=find(vv);}
39     }
40 }
41 
42 int main(){
43     freopen("tarjanlca.txt","r",stdin);
44     memset(head,-1,sizeof(head));
45     memset(heap,-1,sizeof(heap));
46     scanf("%d",&n);
47     for(int i=1;i<=n;i++){
48         int u,v;scanf("%d%d",&u,&v);
49         if(v==-1){root=u;continue;}
50         adde(u,v);adde(v,u);maxid=max(maxid,max(u,v));
51     }
52     for(int i=1;i<=maxid;i++)fa[i]=i;
53     scanf("%d",&m);
54     for(int i=1;i<=m;i++){
55         int u,v;scanf("%d%d",&u,&v);
56         addp(u,v,i);addp(v,u,i);
57     }visid[root]=1;
58     tarjan(root,0);
59     for(int i=1;i<=m;i++){
60         int lca=vis[i],pos=(i-1)*2;
61         if(lca==p[pos].u ) puts("1");
62         else if(lca==p[pos].v )puts("2");
63         else puts("0");
64     }
65 }
View Code

【总结】

tarjan大法好,可惜皮不来啊

转载于:https://www.cnblogs.com/Danzel-Aria233/p/7810704.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值