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
",dist[now.from]+dist[now.to]-2*dist[now.lca]);
132     }
133     return 0;
134 }

后记

赛时迅速的口A了这一题,奈何卡在求树上任两点距离,还算错了500*500,导致RE了十发。然后网上模板都是前向星,然而我不会,还是喜欢邻接表哎,所以赛后我自己改出了邻接表版本哈哈哈,希望以后能够继续多学习算法吧,口A码不出太难过了。向图论大师李老师致敬!

原文地址:https://www.cnblogs.com/computer-luo/p/9622072.html