hdu2887 最小生成树+利用lca倍增求树上两点间最大线段

首先:可以利用加一个虚拟节点连向所有点,边权即为点权,求一遍最小生成树

然后在这棵树上预处理一个倍增,在向上倍增的过程中开一个maxdis[x][j]记录从x开始向上2^j步最大线段

这样就可以实现log的查询了==

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<algorithm>
  4 using namespace std;
  5 struct dian{
  6   int x,y,w;
  7 }a[30005];
  8 int now,head[30005],point[30005],next[30005],value[30005];
  9 int dis[30005],parent[30005][25],depth[30005],father[30005];
 10 int maxdis[30005][25];
 11 int cmp(dian n1,dian n2)
 12 {
 13   return n1.w<n2.w;
 14 }
 15 void add(int x,int y,int w)
 16 {
 17   next[++now]=head[x];
 18   head[x]=now;
 19   point[now]=y;
 20   value[now]=w;
 21 }
 22 int find(int x)
 23 {
 24   if (father[x]!=x) father[x]=find(father[x]);
 25   return father[x];
 26 }
 27 void dfs(int u,int pre,int deep,int d,int prew)
 28 {
 29   dis[u]=d;
 30   parent[u][0]=pre;
 31   depth[u]=deep;
 32   maxdis[u][0]=prew;
 33   for (int i=head[u];i!=-1;i=next[i])
 34   {
 35     int v=point[i],w=value[i];
 36     if (v!=pre) dfs(v,u,deep+1,d+w,w);
 37   }
 38 }
 39 void double_build(int n)
 40 {
 41   int i,j;
 42   for (i=0;i<=20;i++)
 43     for (j=0;j<=n;j++)
 44   if (parent[j][i]<0) parent[j][i+1]=-1,maxdis[j][i+1]=maxdis[j][i];
 45   else{
 46        parent[j][i+1]=parent[parent[j][i]][i];
 47     maxdis[j][i+1]=max(maxdis[j][i],maxdis[parent[j][i]][i]);
 48   }
 49 }
 50 int double_query(int u,int v)
 51 {
 52   int i,maxx=0;
 53   if (depth[u]>depth[v]) swap(u,v);
 54   for (i=0;i<=20;i++)
 55     if ((depth[v]-depth[u])>>i&1)
 56       maxx=max(maxx,maxdis[v][i]),v=parent[v][i];
 57   if (u==v) return maxx;
 58   for (i=20;i>=0;i--)
 59     if (parent[u][i]!=parent[v][i])
 60     {
 61       maxx=max(maxx,maxdis[u][i]);
 62       maxx=max(maxx,maxdis[v][i]);
 63       u=parent[u][i];
 64       v=parent[v][i];
 65     }
 66   return max(max(maxx,maxdis[u][0]),maxdis[v][0]);
 67 }
 68 int main()
 69 {
 70   int i,n,k,q,cnt,x,y,w,f1,f2;
 71   long long sum;
 72   while (~scanf("%d%d%d",&n,&k,&q))
 73   {
 74     cnt=0;
 75     for (i=1;i<=n;i++)
 76     {
 77       scanf("%d",&w);
 78       a[++cnt].x=0; a[cnt].y=i; a[cnt].w=w;
 79     }
 80     for (i=1;i<=k;i++)
 81     {
 82       scanf("%d%d%d",&x,&y,&w);
 83       a[++cnt].x=x; a[cnt].y=y; a[cnt].w=w;
 84     }
 85     for (i=0;i<=n;i++) father[i]=i;
 86     sort(a+1,a+cnt+1,cmp);
 87     sum=now=0;
 88     memset(head,-1,sizeof(head));    
 89     for (i=1;i<=cnt;i++)
 90     {
 91       f1=find(a[i].x); f2=find(a[i].y);
 92       if (f1==f2) continue;
 93       add(a[i].x,a[i].y,a[i].w);
 94       add(a[i].y,a[i].x,a[i].w);
 95       sum=sum+a[i].w;
 96       father[f1]=f2;
 97     }
 98     dfs(0,-1,0,0,0);
 99     double_build(n);
100 //    printf("%lld\n",sum);
101     while (q--)
102     {
103       scanf("%d%d",&x,&y);
104       w=double_query(x,y);
105       printf("%lld\n",sum-w);
106     }
107   }
108   return 0;
109 }
View Code

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2887

转载于:https://www.cnblogs.com/xiao-xin/articles/4278256.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值