首页 > 代码库 > CodeVS 1036 商务旅行

CodeVS 1036 商务旅行

最近一边转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 商务旅行