1. 程式人生 > >CodeVS 1036 商務旅行

CodeVS 1036 商務旅行

height 祖先 memset 商務 最近公共祖先 pri nbsp nsh return

最近一邊轉c++一邊學算法,因為GDOI好像考了LCA,不會寫怎麽辦呢?(當然是爆零得到教訓後再來學啊..)

題目大意:給一棵樹,求經過給定結點的最小費用。。

算法:帶權LCA?倍增LCA?(然而我都不會。。)我寫了個LCA+SPFA。。rp++地水過了。。

技術分享技術分享

看到樣例。。先畫了一個圖。。

技術分享

因為只有n-1條邊,所以這肯定不是連通圖,是一棵多叉樹..(自行證明。。)

於是跟著樣例的路線走一遍,顯然發現跑m遍spfa會tle(可能吧。。),然而發現相鄰城市的距離就會是兩個城市到它的LCA(最近公共祖先)的距離,那麽我萌就只要用spfa預處理出根節點到所有節點的距離,然後求相鄰城市的LCA。。每次把答案加上兩個城市到根節點的距離減去LCA到根節點距離*2。。(等於兩個結點的距離)

證一波:

結點2和3的最近公共祖先是1,2到根節點距離為1,3到根節點距離為2,1到根節點距離為0,2到3的距離就是1+2-2*0=3.(即橙色段的和減去藍色段的和。。)

那麽就可以寫了。。我寫的是用tarjan離線求LCA。。(在線ST算法搞不懂。。)

技術分享
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <algorithm>
  4 #include <cstring>
  5 using namespace std;
  6  int n,m,s,e,tot;
  7  int
dis[100001],toit[100001],cost[100001],next[100001],lists[100001],q[100001]; 8 bool flag[100001]; 9 #define maxint int(1e+9) 10 struct point 11 { 12 int to,next; 13 } 14 edge[10000000]; 15 int head[510000],idx=0; 16 bool vis[510000]; 17 struct anspoint 18 { 19 int to,next,num; 20 } 21 ansedge[10000000]; 22
int anshead[510000],ansidx=0,ans[5100000],group[510000]; 23 void adds(int a,int b,int c) 24 { 25 tot++; 26 toit[tot]=b; 27 cost[tot]=c; 28 next[tot]=lists[a]; 29 lists[a]=tot; 30 } 31 void spfa(int s)//spfa 32 { 33 int i,head,tail,v,k; 34 memset(flag,true,sizeof(flag)); 35 for (i=1;i<=n;i++) 36 dis[i]=maxint; 37 head=tail=1; 38 q[1]=s; dis[s]=0; flag[s]=false; 39 while (head<=tail) { 40 v=q[head]; 41 k=lists[v]; 42 while (k!=0) { 43 if (dis[v]+cost[k]<dis[toit[k]] ){ 44 dis[toit[k]]=dis[v]+cost[k]; 45 if( flag[toit[k]] ){ 46 tail++; 47 q[tail]=toit[k]; 48 flag[toit[k]]=false; 49 50 } 51 } 52 k=next[k]; 53 } 54 flag[v]=true; 55 head++; 56 } 57 } 58 void add(int u,int v) 59 { 60 edge[++idx].to=v; 61 edge[idx].next=head[u]; 62 head[u]=idx; 63 } 64 void ansadd(int u,int v,int num) 65 { 66 ansedge[++ansidx].to=v; 67 ansedge[ansidx].next=anshead[u]; 68 ansedge[ansidx].num=num; 69 anshead[u]=ansidx; 70 } 71 int find(int x) 72 { 73 int pre=x,y; 74 while (x!=group[x]) 75 x=group[x]; 76 while (pre!=group[pre]) 77 { 78 y=group[pre]; 79 group[pre]=x; 80 pre=y; 81 } 82 return x; 83 } 84 void tarjan(int x)//tarjan 離線求lca 85 { 86 vis[x]=1; 87 group[x]=x; 88 for (int i=head[x];i!=-1;i=edge[i].next) 89 { 90 int now=edge[i].to; 91 if (!vis[now]) 92 { 93 tarjan(now); 94 group[now]=x; 95 } 96 } 97 for (int i=anshead[x];i!=-1;i=ansedge[i].next) 98 { 99 int now=ansedge[i].to,num=ansedge[i].num; 100 if (vis[now])ans[num]=find(now); 101 } 102 } 103 //} 104 int main() 105 { 106 memset(head,-1,sizeof(head)); 107 memset(anshead,-1,sizeof(anshead)); 108 int x,y,anss; 109 int p[10000]; 110 scanf("%d",&n); 111 for (int i=1;i<n;++i) 112 { 113 scanf("%d%d",&x,&y); 114 add(x,y); add(y,x); 115 adds(x,y,1); adds(y,x,1); 116 } 117 scanf("%d",&m); 118 for (int i=1;i<=m;++i) 119 scanf("%d",&p[i]); 120 for (int i=1;i<=m;++i) 121 { 122 if (i!=m ){ 123 ansadd(p[i],p[i+1],i); 124 ansadd(p[i+1],p[i],i); 125 } 126 } 127 tarjan(1);//從根節點開始 128 spfa(1); 129 anss=0; 130 for (int i=1;i<m;i++) 131 { 132 //printf("%d\n",dis[p[i]]+dis[p[i+1]]-(2*dis[ans[i]])); 133 anss=anss+dis[p[i]]+dis[p[i+1]]-(2*dis[ans[i]]); 134 } 135 printf("%d\n",anss); 136 } 137
View Code

最後雖然寫了130+行。。在各位dalao幫助下調了兩個小時(剛轉c++。。),最後還是一次AC。。(本以為用spfa時間會卡的很緊,結果跑了76ms。。)

至於題解裏什麽倍增LCA還是要去學一學的。。

剛剛轉c++,如果各位dalao覺得我代碼哪裏寫的醜還是寫的不好的請指出。。=v=

CodeVS 1036 商務旅行