lca

讲得不错的

有参考过的

一点经验:如果是多次实时查询用rmq

如果是一次性全部输入则用tarjan 

倍增法:求lca看一下。

 

poj,1330

最近公共祖先的tarjan离线算法,一次性批处理,然后再query

注意建边,注意访问,注意基于dfs,注意寻找根节点,并查集的运用;

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <string>
 5 #include <vector>
 6 
 7 using namespace std;
 8 const int maxn=10005;
 9 
10 int n,u,v;
11 int parent[maxn];
12 
13 struct Edge
14 {
15     bool notroot;
16     bool vis;
17     vector<int> children;
18 };
19 
20 Edge Tree[maxn];
21 
22 
23 int find(int x)
24 {
25     return x==parent[x]?x:parent[x]=find(parent[x]);
26 }
27 
28 void Union(int x,int y)
29 {
30     int px=find(x);
31     int py=find(y);
32     
33     if(px!=py)
34         parent[py]=px;
35 }
36 
37 bool tarjan(int root)
38 {
39     Tree[root].vis=true;
40     
41     if(root==v && Tree[u].vis)
42     {
43         printf("%d\n",find(u));
44         return true;
45     }
46     
47     if(root==u && Tree[v].vis)
48     {
49         printf("%d\n",find(v));
50         return true;
51     }
52     
53     for(int i=0;i<Tree[root].children.size();i++)
54     {
55         if(tarjan(Tree[root].children[i])) return true;
56         Union(root,Tree[root].children[i]);
57     }
58     return false;
59 }
60 
61 void initial()
62 {
63     memset(parent,0,sizeof(parent));
64     memset(Tree,0,sizeof(Tree));
65     
66     scanf("%d",&n);
67     for(int i=1;i<n;i++)
68     {
69         parent[i]=i;
70         scanf("%d%d",&u,&v);
71         Tree[u].children.push_back(v);
72         Tree[v].notroot=1;
73     }
74     parent[n]=n;
75     
76     int root=0;
77     for(int i=1;i<=n;i++)
78     {
79         if(!Tree[i].notroot)
80         {
81             root=i;
82             break;
83         }
84     }
85     scanf("%d%d",&u,&v);
86     tarjan(root);
87 }
88 
89 int main()
90 {
91     int t;
92     scanf("%d",&t);
93     while(t--)
94     {
95         initial();
96     }
97     return 0;
98 }
View Code

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <string>
 4 #include <vector>
 5 #include <algorithm>
 6 
 7 using namespace std;
 8 const int maxn=10005;
 9 
10 int n,u,v;
11 struct Node
12 {
13     bool notroot;
14     vector<int> children;
15 };
16 
17 Node Tree[maxn];
18 int N;
19 
20 int find(int r,int lNode,int rNode)
21 {
22     if(!r) return 0;
23     if(r==lNode) return r;
24     if(r==rNode) return r;
25     
26     vector<int> found;
27     
28     for(int i=0;i<Tree[r].children.size();i++)
29     {
30         found.push_back(find(Tree[r].children[i],lNode,rNode));
31     }
32     
33     int u=0,v=0;
34     for(int i=0;i<(int)found.size();i++)
35     {
36         if(u) v=found[i];
37         else u=found[i];
38     }
39     
40     if(v) return r;
41     return u;
42 }
43 
44 void initial()
45 {
46     memset(Tree,0,sizeof(Tree));
47     scanf("%d",&n);
48     for(int i=1;i<n;i++)
49     {
50         scanf("%d%d",&u,&v);
51         Tree[u].children.push_back(v);
52         Tree[v].notroot=1;
53     }
54     
55     int root=0;
56     
57     for(int i=1;i<=n;i++)
58     {
59         if(!Tree[i].notroot)
60         {
61             root=i;
62             break;
63         }
64     }
65     
66     scanf("%d%d",&u,&v);
67     int r=find(root,u,v);
68     printf("%d\n",r);
69 }
70 
71 int main()
72 {
73     int t;
74     scanf("%d",&t);
75     while(t--)
76     {
77         initial();
78     }
79     return 0;
80 }
递归版

 

代码复用技术啊~~~

注意RMQ与lca的相互关系;

定义三个数组:n代表节点数,lca转化为RMQ问题之后,数组长度为n*2+1

E[n*2+2]  第一次被访问和回溯时被访问的节点

D[n*2+2]  记录此节点的深度

R[n*2+2]  记录节点第一访问的下标

dfs:深搜初始化E,D,R的值。

RMQ:求(i,j)上值最小的索引,分成两段区间。

ST:初始化(i,j)区间上值最小的索引

E[ ( RMQ(D,R[i],R[j] ) ] 就求出了最近公共最先,也是一段序列里面最小的数,层次最低。注意:R[i]<R[j]要不然要交换位置

 

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <vector>
  4 #include <cmath>
  5 
  6 #define min(seq,a,b) ((seq[a]<seq[b])?(a):(b))
  7 using namespace std;
  8 
  9 const int maxn=10005;
 10 
 11 vector<int> children[maxn];
 12 int notroot[maxn];
 13 int M[2*maxn+5][100];
 14 
 15 template <typename T>
 16 void st(T seq[],int n)
 17 {
 18     memset(M,0,10*maxn*sizeof(int));
 19     int i,j;
 20     for(i=0;i<n;i++)
 21         M[i][0]=i;
 22     
 23     for(j=1;1<<j<=n;j++)
 24         for(i=0;i+(1<<j)-1<n;i++)
 25         {
 26             int m=i+(1<<(j-1));
 27             M[i][j]=min(seq,M[i][j-1],M[m][j-1]);
 28         }
 29 }
 30 
 31 template <typename T>
 32 int RMQ(T seq[],int i,int j)
 33 {
 34     int k=(int)(log(double(j-i+1))/log(2.0));
 35     
 36     int t=min(seq,M[i][k],M[j-(1<<k)+1][k]);
 37     return t;
 38 }
 39 
 40 int E[2*maxn+5];
 41 int R[2*maxn+5];
 42 int D[2*maxn+5];
 43 int p;
 44 
 45 void dfs(int r,int deep)
 46 {
 47     p++;
 48     E[p]=r;
 49     D[p]=deep;
 50     R[r]=p;
 51     
 52     for(int i=0;i<children[r].size();i++)
 53     {
 54         dfs(children[r][i],deep+1);
 55         p++;
 56         E[p]=r;
 57         D[p]=deep;
 58     }
 59 }
 60 
 61 int main()
 62 {
 63     int t;
 64     scanf("%d",&t);
 65     while(t--)
 66     {
 67         int n;
 68         int u,v;
 69         scanf("%d",&n);
 70         
 71         for(int i=0;i<=n;i++)
 72         {
 73             notroot[i]=0;
 74             children[i].clear();
 75         }
 76         
 77         for(int i=1;i<n;i++)
 78         {
 79             scanf("%d%d",&u,&v);
 80             children[u].push_back(v);
 81             notroot[v]=1;
 82         }
 83         
 84         scanf("%d%d",&u,&v);
 85         
 86         int root=0;
 87         for(int i=1;i<=n;i++)
 88             if(!notroot[i])
 89             {
 90                 root=i;
 91                 break;
 92             }
 93         p=0;
 94         dfs(root,0);
 95         st(D,2*n+2);
 96         
 97         if(R[u]<R[v])
 98             printf("%d\n",E[RMQ(D,R[u],R[v])]);
 99         else
100             printf("%d\n",E[RMQ(D,R[v],R[u])]);
101     }
102     return 0;
103 }
RMQ重写了

 

 

HDU,2586

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <algorithm>
  4 #include <vector>
  5 
  6 using namespace std;
  7 const int maxn=40005;
  8 
  9 vector<int> v[maxn],w[maxn],ans_num[maxn],query[maxn];
 10 //v记录儿子节点数,w纪录两节点之间的权值,ans_num纪录此时是第几个问
 11 //题,query纪录询问那两个节点
 12 int ans[205],parent[maxn],notroot[maxn],dist[maxn];
 13 int n,m;
 14 bool vis[maxn] ;
 15 
 16 void initial()
 17 {//初始化
 18     for(int i=1;i<=n;i++)
 19     {
 20         v[i].clear();
 21         w[i].clear();
 22         ans_num[i].clear();
 23         query[i].clear();
 24         notroot[i]=0;
 25         dist[i]=0;
 26         vis[i]=false;
 27         parent[i]=i;
 28     }
 29     memset(ans,0,sizeof(ans));
 30 }
 31 
 32 int find(int x)
 33 {
 34     return x==parent[x]?x:parent[x]=find(parent[x]);
 35 }
 36 
 37 void Union(int x,int y)
 38 {
 39     parent[find(y)]=find(x);
 40 }
 41 
 42 void tarjan(int now,int value)
 43 {
 44     vis[now]=true;
 45     dist[now]=value;
 46     
 47     for(int i=0;i<v[now].size();i++)
 48     {
 49         int tmp=v[now][i];
 50         if(vis[tmp]) continue;
 51         tarjan(tmp,value+w[now][i]);
 52         Union(now,tmp);
 53     }
 54     
 55     for(int i=0;i<query[now].size();i++)
 56     {
 57         int tmp=query[now][i];
 58         if(!vis[tmp]) continue;
 59         ans[ans_num[now]
 60 //找到每个点到根节点的距离,然后找到用两个点的距离减去最近公共祖先的
 61 //距离两倍即为两个点之间的距离。
 62 [i]]=dist[now]+dist[tmp]-2*dist[find(tmp)];
 63     }
 64 }
 65 
 66 int main()
 67 {
 68     int t;
 69     scanf("%d",&t);
 70     while(t--)
 71     {
 72         scanf("%d%d",&n,&m);
 73         
 74         initial();
 75         int a,b,l;
 76         for(int i=1;i<n;i++)
 77         {
 78             scanf("%d%d%d",&a,&b,&l);
 79             v[a].push_back(b);
 80             v[b].push_back(a);
 81             w[a].push_back(l);
 82             w[b].push_back(l);
 83             notroot[b]=1;  //为找出根节点做准备;
 84         }
 85         
 86         for(int i=0;i<m;i++)
 87         {
 88             scanf("%d%d",&a,&b);
 89             query[a].push_back(b);
 90             query[b].push_back(a);
 91             ans_num[a].push_back(i);
 92             ans_num[b].push_back(i);
 93         }
 94         
 95         int root=0;
 96         for(int i=1;i<=n;i++)
 97             if(!notroot[i])  
 98             {
 99                 root=i;
100                 break;
101             }
102         tarjan(root,0);
103         
104         for(int i=0;i<m;i++)
105             printf("%d\n",ans[i]);
106     }
107     return 0;
108 }
View Code

 

zoj,2838

我们考虑abc  c在a到b之间,如果a和b不是同一颗子树上的,c是a和b的最近公共祖先才能保证她们能遇到

如果a和b在同一颗子树上,那么用 fac==c || fbc==c且只有一个是对的就可以判断c在a和b之间。

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <vector>
  4 #include <cmath>
  5 #include <algorithm>
  6 #include <cstdlib>
  7 #include <cstring>
  8 
  9 #define min(seq,a,b) ((seq[a]<seq[b])?(a):(b))
 10 using namespace std;
 11 
 12 const int maxn=50005;
 13 
 14 vector<int> children[maxn];
 15 int notroot[maxn];
 16 int M[maxn*2+5][30];
 17 
 18 void st(int seq[],int n)
 19 {
 20     memset(M,0,10*maxn*sizeof(int));
 21     int i,j;
 22     for(i=0;i<n;i++)
 23         M[i][0]=i;
 24     
 25     for(j=1;1<<j<=n;j++)
 26         for(i=0;i+(1<<j)-1<n;i++)
 27         {
 28             int m=i+(1<<(j-1));
 29             M[i][j]=min(seq,M[i][j-1],M[m][j-1]);
 30         }
 31 }
 32 
 33 int RMQ(int seq[],int i,int j)
 34 {
 35     if(i>j)
 36         swap(i,j);
 37     int k=(int)(log(double(j-i+1))/log(2.0));
 38     
 39     int t=min(seq,M[i][k],M[j-(1<<k)+1][k]);
 40     return t;
 41 }
 42 
 43 int E[maxn*2+5];
 44 int R[maxn*2+5];
 45 int D[maxn*2+5];
 46 int p;
 47 
 48 void dfs(int r,int deep)
 49 {
 50     p++;
 51     E[p]=r;
 52     D[p]=deep;
 53     R[r]=p;
 54     
 55     for(int i=0;i<children[r].size();i++)
 56     {
 57         dfs(children[r][i],deep+1);
 58         p++;
 59         E[p]=r;
 60         D[p]=deep;
 61     }
 62 }
 63 
 64 int main()
 65 {
 66     int n;
 67     int cas=1;
 68     while(~scanf("%d",&n))
 69     {
 70         int m;
 71         int u,v,w;
 72         
 73         for(int i=0;i<=n;i++)
 74         {
 75             notroot[i]=0;
 76             children[i].clear();
 77         }
 78         
 79         for(int i=1;i<n;i++)
 80         {
 81             scanf("%d%d",&u,&v);
 82             u++;v++;
 83             children[u].push_back(v);
 84             notroot[v]=1;
 85         }
 86         
 87         int root=0;
 88         for(int i=1;i<=n;i++)
 89             if(!notroot[i])
 90             {
 91                 root=i;
 92                 break;
 93             }
 94         p=0;
 95         dfs(root,0);
 96         st(D,2*n+2);
 97         
 98         if(cas>1)
 99             printf("\n");
100         scanf("%d",&m);
101         printf("Case %d:\n",cas++);
102         for(int i=0;i<m;i++)
103         {
104             scanf("%d%d%d",&u,&v,&w);
105             u++;v++;w++;
106             int fxy=E[RMQ(D,R[u],R[v])];
107             int fxz=E[RMQ(D,R[u],R[w])];
108             int fyz=E[RMQ(D,R[v],R[w])];
109             
110             if(fxz==w && fyz==w)
111             {
112                 if(fxy==w)
113                     printf("Yes\n");
114                 else
115                     printf("No\n");
116             }
117             else
118             {
119                 if(fxz==w || fyz==w)
120                     printf("Yes\n");
121                 else
122                     printf("No\n");
123             }
124         }
125     }
126     return 0;
127 }
View Code

 

ZOJ,3664

这题🐃🆚🐂,参考了别人的代码,并做了自己的相关感想和注解。学习了。

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cstring>
  5 #include <algorithm>
  6 
  7 #define maxn 3000
  8 using namespace std;
  9 
 10 
 11 struct Point{  //纪录target point
 12     int x,y;
 13 }A,B;
 14 
 15 struct Rect   //纪录正方形集合
 16 {
 17     int x1,x2,y1,y2;
 18     int l,r,sonCnt;   //l,r为左右子树在R中的索引,sonCnt为正方形中小
 19 //正方形的个数
 20 }R[maxn];
 21 
 22 struct Segment  //创建线段
 23 {
 24     int x1,x2,y1,y2;
 25 }p[maxn];
 26 
 27 int N,Q,tot;
 28 
 29 bool innerLine(int v,int L,int R)  //线段是否在正方体内部
 30 {
 31     return (v>=L && v<=R);
 32 }
 33 
 34 int buildTree(Rect node,int flag)  //建立二叉树
 35 {
 36     R[tot]=node;  //初始化
 37     R[tot].l=R[tot].r=-1;
 38     R[tot].sonCnt=0;
 39     int res=tot++;
 40     
 41     for(int i=0;i<N;i++)
 42         if((p[i].y1==p[i].y2)^flag)  //异或运算,根据题目意思
 43         {//当为初始化或有竖线只能建横线/否则建竖线...巧妙的破解
 44             if(!flag)
 45             {//建横线
 46                 if(p[i].x1==node.x1 && p[i].x2==node.x2 && innerLine(p[i].y1,node.y1,node.y2))
 47                 {//因为分出的两个小正方形的x坐标一致,只要改变y坐标,即可
 48 //建立两子树节点
 49                     Rect temp=node;
 50                     temp.y1=p[i].y1;
 51                     R[res].l=buildTree(temp,!flag);
 52                     
 53                     
 54                     temp=node;
 55                     temp.y2=p[i].y2;
 56                     R[res].r=buildTree(temp,!flag);
 57                 }
 58             }
 59             else
 60             {//同理,建竖线段
 61                 if(p[i].y1==node.y1 && p[i].y2==node.y2 && innerLine(p[i].x1,node.x1,node.x2))
 62                 {
 63                     Rect temp=node;
 64                     temp.x1=p[i].x1;
 65                     R[res].l=buildTree(temp,!flag);
 66                     
 67                     temp=node;
 68                     temp.x2=p[i].x2;
 69                     R[res].r=buildTree(temp,!flag);
 70                 }
 71             }
 72         }
 73     if(R[res].l==-1 && R[res].r==-1) R[res].sonCnt=1;
 74     else
 75     {//递归计算正方形里面的小正方形的个数
 76         if(R[res].l!=-1) R[res].sonCnt=R[R[res].l].sonCnt;
 77         if(R[res].r!=-1) R[res].sonCnt+=R[R[res].r].sonCnt;
 78     }
 79     return res;
 80 }
 81 //是否在正方形中
 82 bool innerRect(Point C,Rect R)
 83 {
 84     return (C.x>=R.x1 && C.x<=R.x2 && C.y>=R.y1 && C.y<=R.y2);
 85 }
 86 
 87 int dfs(Rect node)
 88 {//寻找lca
 89     if(node.l!=-1)
 90         if(innerRect(A,R[node.l]) && innerRect(B,R[node.l]))
 91             return dfs(R[node.l]);
 92     if(node.r!=-1)
 93         if(innerRect(A,R[node.r]) && innerRect(B,R[node.r]))
 94             return dfs(R[node.r]);
 95     return node.sonCnt;
 96 }
 97 
 98 int main()
 99 {
100     while(cin>>R[0].x1>>R[0].y1>>R[0].x2>>R[0].y2)
101     {
102         cin>>N>>Q;
103         
104         for(int i=0;i<N;i++)
105         {
106             scanf("%d%d%d%d",&p[i].x1,&p[i].y1,&p[i].x2,&p[i].y2);
107             if(p[i].x1>p[i].x2) swap(p[i].x1,p[i].x2);
108             if(p[i].y1>p[i].y2) swap(p[i].y1,p[i].y2);
109         }
110         tot=0;
111         buildTree(R[0],0);
112         
113         for(int i=0;i<Q;i++)
114         {//加了N条线段,有N+1个正方形,减去此时lca的子正方形个数+
115 //新产生的正方形即为答案。
116             scanf("%d%d%d%d",&A.x,&A.y,&B.x,&B.y);
117             cout<<N+2-dfs(R[0])<<endl;
118         }
119     }
120     return 0;
121 }
View Code

 

posted @ 2016-04-26 23:17  指尖泛出的繁华  阅读(221)  评论(0)    收藏  举报