ACM-ICPC 2018 徐州赛区网络预赛 J. Maze Designer <<LCA+tarjin离线+最小生成树(邻接表储存)
题意
在一个n*m的地图的线上造迷宫,给出每条边的造价,造出迷宫,使得地图上任两点有且仅有一条路,使迷宫造价最低,题目保证这样的迷宫唯一。建出迷宫之后有q次询问,询问两点间的距离。
思路
迷宫构造问题,起初没有思路,在一段思索之后和鸡神想出了反其道而行之的办法,先假设所有墙都有,我们要满足题设条件就要砸墙,每次砸最贵的墙,构成一颗生成树,所以就是建造这个迷宫就是求一个最大生成树的问题了,在此选用了kruskal算法。
而后把这棵树存下来,然后就是想办法求树上任两点距离就行了,方法很多,但我和鸡神一种都不会,所以赛时我们就僵住了,最后我们考虑到q有10000次之大,所以我们跑了个离线算法(LCA+tarjin),当然此题RMQ,ST表好像都是可以跑的。
关于tarjin+LCA之后放一道裸题详细分析,此题代码就不给注释了。
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef pair<int,int> PII; 4 const int N=500+7; 5 struct edge{ 6 int from,to,w; 7 }; 8 struct askedge{ 9 int from,to,lca; 10 }ae[N*N]; 11 edge mazee[N*N*4]; 12 int kfar[N*N]; 13 vector<int> G[N*N]; 14 vector<PII> pp[N*N]; 15 int tot=0; 16 int n,m; 17 void add_edge(int from,int to,int w) 18 { 19 mazee[tot].from=from; 20 mazee[tot].to=to; 21 mazee[tot].w=w; 22 mazee[++tot].from=to; 23 mazee[tot].to=from; 24 mazee[tot].w=w; 25 } 26 void init() 27 { 28 for(int i=0;i<N*N;i++) 29 kfar[i]=i; 30 } 31 bool cmp(edge a,edge b) 32 { 33 return a.w>b.w; 34 } 35 int Find(int x) 36 { 37 if(kfar[x]==x) return x; 38 else return kfar[x]=Find(kfar[x]); 39 } 40 void Union(int x,int y) 41 { 42 x=Find(x);y=Find(y); 43 if(x!=y) kfar[y]=x; 44 } 45 void kruskal() 46 { 47 sort(mazee,mazee+tot+1,cmp); 48 int cnt=0; 49 for(int i=0;i<n*m*4;i++) 50 { 51 edge &u=mazee[i]; 52 if(Find(u.from)!=Find(u.to)) 53 { 54 Union(u.from,u.to); 55 G[u.from].push_back(u.to); 56 G[u.to].push_back(u.from); 57 cnt++; 58 } 59 if(cnt==n*m-1) break; 60 } 61 } 62 int far[N*N]; 63 int dist[N*N]; 64 int tarjinfind(int x) 65 { 66 if(far[x]==x) return x; 67 else return far[x]=tarjinfind(far[x]); 68 } 69 void tarjinunion(int x,int y) 70 { 71 x=tarjinfind(x);y=tarjinfind(y); 72 if(x!=y) far[y]=x; 73 } 74 void dfs(int now) 75 { 76 far[now]=now; 77 for(int i=0;i<pp[now].size();i++) 78 { 79 PII &v=pp[now][i]; 80 if(dist[v.first]!=-1) 81 { 82 ae[v.second].lca=tarjinfind(v.first); 83 } 84 } 85 for(int i=0;i<G[now].size();i++) 86 { 87 int &v=G[now][i]; 88 if(dist[v]==-1) 89 { 90 dist[v]=dist[now]+1; 91 dfs(v); 92 tarjinunion(now,v); 93 } 94 } 95 } 96 int main() 97 { 98 scanf("%d%d",&n,&m); 99 for(int i=1;i<=n;i++) 100 { 101 for(int j=1;j<=m;j++) 102 { 103 char opd,opr; 104 int down,right; 105 scanf(" %c%d %c%d",&opd,&down,&opr,&right); 106 if(opd!='X') 107 add_edge((i-1)*m+j,i*m+j,down); 108 if(opr!='X') 109 add_edge((i-1)*m+j,(i-1)*m+j+1,right); 110 } 111 } 112 init(); 113 kruskal(); 114 int q; 115 scanf("%d",&q); 116 for(int i=1;i<=q;i++) 117 { 118 int x1,y1,x2,y2; 119 scanf("%d%d%d%d",&x1,&y1,&x2,&y2); 120 int u=m*(x1-1)+y1,v=m*(x2-1)+y2; 121 pp[u].push_back(make_pair(v,i)); 122 pp[v].push_back(make_pair(u,i)); 123 ae[i]={u,v,-1}; 124 } 125 memset(dist,-1,sizeof(dist)); 126 dist[1]=0; 127 dfs(1); 128 for(int i=1;i<=q;i++) 129 { 130 askedge &now=ae[i]; 131 printf("%d\n",dist[now.from]+dist[now.to]-2*dist[now.lca]); 132 } 133 return 0; 134 }
后记
赛时迅速的口A了这一题,奈何卡在求树上任两点距离,还算错了500*500,导致RE了十发。然后网上模板都是前向星,然而我不会,还是喜欢邻接表哎,所以赛后我自己改出了邻接表版本哈哈哈,希望以后能够继续多学习算法吧,口A码不出太难过了。向图论大师李老师致敬!