标签:lis 蓝色 算法 cst lin iostream include 连通图 als
最近一边转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
最后虽然写了130+行。。在各位dalao帮助下调了两个小时(刚转c++。。),最后还是一次AC。。(本以为用spfa时间会卡的很紧,结果跑了76ms。。)
至于题解里什么倍增LCA还是要去学一学的。。
刚刚转c++,如果各位dalao觉得我代码哪里写的丑还是写的不好的请指出。。=v=
标签:lis 蓝色 算法 cst lin iostream include 连通图 als
原文地址:http://www.cnblogs.com/Watton/p/6853011.html