CF1051F The Shortest Statement
题意:给你一个无向图,有n个点m条边,有t个询问,求询问的两个点的最短距离.
思路分析:这道题乍一看十分像一道板子题,但是如果你真的把他当板子题来写你就T得爽歪歪了(滑稽~)。
题目中有一个十分关键的信息,m和n的差不超过20,基于这个信息,我们可以把图分为一棵树和最多20条边,那么我们对待一个询问x,y;可以考虑到有两种情况,一种是最短路的所有路径都在树上,那么我们跑一个LCA即可,那么如果最短路一部分在其他的边上呢?我们只能跑一遍dijkstra(关于SPFA?它死了)来求解,极限情况下我们最多只跑了40遍dijkstra(20条边40个点),相比之下已经快许多了,由于不在树上的路径一定会过路径两边的点,我们只需对每一个相应点跑dijkstra,对于询问比较每个点的情况,和LCA比较求出最小值即可。
思路说完了,来说一下我调代码时跳的坑。
1.带longlong的题min函数要自己写,不然会错
2.lca的depth是深度,不是距离.
3.求最小生成树并查集一定要初始化,不然优化就没意义了
4.longlong值以int输出会发生奇妙的错误
5.建的两张图的变量不要弄混了
希望可以帮到调代码遇到困难的各位,调代码调不出来太痛苦了。。。
上代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<queue> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 typedef long long ll; 6 using namespace std; 7 const int N=2e5+10; 8 ll Min(ll x,ll y){ 9 return x<y?x:y; 10 } 11 struct Node{ 12 ll next,to; 13 ll dis; 14 }edge[N],tree[N]; 15 ll n,m,v[N]; 16 ll Head_edge[N],Head_tree[N],tot_edge,tot_tree; 17 void Add_edge(ll x,ll y,ll z){ //生成原图 18 edge[++tot_edge].dis=z; 19 edge[tot_edge].to=y; 20 edge[tot_edge].next=Head_edge[x]; 21 Head_edge[x]=tot_edge; 22 } 23 void Add_tree(ll x,ll y,ll z){ //最小生成树,用于求LCA 24 tree[++tot_tree].dis=z; 25 tree[tot_tree].to=y; 26 tree[tot_tree].next=Head_tree[x]; 27 Head_tree[x]=tot_tree; 28 } 29 ll pa[N],dep[N]; 30 ll find(ll x){ //并查集求最小生成树 31 return pa[x]==x?x:(pa[x]=find(pa[x])); 32 } 33 void merge(ll x,ll y){ 34 pa[find(x)]=find(y); 35 } 36 void Init(ll n){ 37 for(ll i=1;i<=n;++i) 38 pa[i]=i; 39 } 40 struct Edge{ //dijkstra部分 41 ll dis; 42 ll num; 43 Edge(ll a,ll b){ 44 dis=a;num=b; 45 } 46 bool operator < (const Edge &a)const{ 47 return a.dis<dis; 48 } 49 }; 50 ll dis[101][N]; 51 ll vis_dij[N],id[N]; 52 priority_queue<Edge>q; 53 void dijkstra(ll x){ 54 memset(dis[id[x]],0x7f,sizeof(dis[id[x]])); 55 memset(vis_dij,0,sizeof(vis_dij)); 56 dis[id[x]][x]=0;q.push(Edge(0,x)); 57 while(!q.empty()){ 58 Edge u=q.top();q.pop(); 59 if(vis_dij[u.num]) continue; 60 vis_dij[u.num]=1; 61 for(ll i=Head_edge[u.num];i;i=edge[i].next){ 62 ll v=edge[i].to; 63 if(dis[id[x]][v]>dis[id[x]][u.num]+edge[i].dis){ 64 dis[id[x]][v]=dis[id[x]][u.num]+edge[i].dis; 65 q.push(Edge(dis[id[x]][v],v)); 66 } 67 } 68 } 69 } 70 ll fa[N],p[N][21]; 71 ll depth[N]; 72 void dfs(ll x,ll pat){ //在生成树上求LCA 73 p[x][0]=fa[x]; 74 depth[x]=depth[fa[x]]+1; 75 for(ll j=0;p[x][j]!=0;++j) 76 p[x][j+1]=p[p[x][j]][j]; 77 for(ll i=Head_tree[x];i;i=tree[i].next){ 78 ll v=tree[i].to; 79 if(v==pat) continue; 80 fa[v]=x; 81 dep[v]=dep[x]+tree[i].dis; 82 dfs(v,x); 83 } 84 } 85 ll lca(ll u,ll v){ 86 if(depth[u]<depth[v]) swap(u,v); 87 ll d=depth[u]-depth[v]; 88 for(ll j=0;d;d>>=1,j++){ 89 if(d&1) u=p[u][j]; 90 } 91 if(u==v) return u; 92 for(ll j=20;j>=0;--j){ 93 if(p[u][j]!=p[v][j]){ 94 u=p[u][j];v=p[v][j]; 95 } 96 } 97 return fa[u]; 98 } 99 struct tre{ //用于sort 100 ll dis; 101 ll from,to; 102 bool operator < (const tre &a)const{ 103 return a.dis<dis; 104 } 105 }tr[N]; 106 ll e[N],book[N]; 107 int main(){ 108 scanf("%lld%lld",&n,&m); 109 Init(n*2); 110 for(ll i=1;i<=m;++i){ 111 ll x,y; 112 ll z; 113 scanf("%lld%lld%lld",&x,&y,&z); 114 Add_edge(x,y,z);Add_edge(y,x,z); //建原图 115 tr[i].from=x;tr[i].to=y;tr[i].dis=z; 116 } 117 sort(tr+1,tr+1+m); 118 for(ll i=1;i<=m;++i){ 119 tre u=tr[i]; 120 ll from=u.from,to=u.to; 121 ll dist=u.dis; 122 if(find(from)!=find(to)){ 123 Add_tree(from,to,dist);//建树 124 Add_tree(to,from,dist); 125 merge(from,to); 126 } 127 else{ 128 e[from]=e[to]=1; //标记树之外的边 129 if(!id[from]){ //数组开不下需要离散化 130 id[from]=++book[0]; 131 book[book[0]]=from; 132 } 133 if(!id[to]){ 134 id[to]=++book[0]; 135 book[book[0]]=to; 136 } 137 } 138 } 139 dfs(1,0); 140 ll t; 141 for(ll i=1;i<=book[0];++i) 142 dijkstra(book[i]); //对树之外的边的每个点求最短路 143 scanf("%lld",&t); 144 for(ll i=1;i<=t;++i){ 145 ll x,y; 146 scanf("%lld%lld",&x,&y); 147 ll Lca=lca(x,y); 148 ll ans=dep[x]+dep[y]-2*dep[Lca];//比较答案 149 for(ll i=1;i<=book[0];++i) 150 ans=Min(ans,dis[i][x]+dis[i][y]); 151 printf("%lld\n",ans); 152 } 153 return 0; 154 }