lca
一点经验:如果是多次实时查询用rmq
如果是一次性全部输入则用tarjan
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 }
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 }
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 }
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 }
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 }
活在现实,做在梦里。

浙公网安备 33010602011771号