POJ 2831 Can We Build This One:次小生成树【N^2预处理】

题目链接:http://poj.org/problem?id=2831

题意:

  给你一个图,每条边有边权。

  然后有q组询问(i,x),问你如果将第i条边的边权改为x,这条边是否有可能在新的最小生成树中。

 

题解:

  更改边权相当于新添加了一条边。

  新边在新MST中的充要条件是:

    加入新边后,在原来的MST上形成的环中,有一条旧边的边权>=x。

    (因为如果这样的话,新边可以替换掉那条最大的边)

 

  所以可以预处理出 maxn[i][j]:在原来的MST上,任意两点间路径上的最大边权。

  dfs即可。

  对于每一个新访问到的节点i,枚举每一个已访问过的节点j,那么:

    maxn[i][j] = maxn[j][i] = max(maxn[j][fa[i]], v[fa[i]][i])

  复杂度O(N^2)。

 

AC Code:

  1 #include <iostream>
  2 #include <stdio.h>
  3 #include <string.h>
  4 #include <algorithm>
  5 #include <vector>
  6 #define MAX_N 1005
  7 #define MAX_M 100005
  8 
  9 using namespace std;
 10 
 11 struct E
 12 {
 13     int s;
 14     int t;
 15     int len;
 16     E(int _s,int _t,int _len)
 17     {
 18         s=_s;
 19         t=_t;
 20         len=_len;
 21     }
 22     E(){}
 23     friend bool operator < (const E &a,const E &b)
 24     {
 25         return a.len<b.len;
 26     }
 27 };
 28 
 29 struct Edge
 30 {
 31     int dest;
 32     int len;
 33     Edge(int _dest,int _len)
 34     {
 35         dest=_dest;
 36         len=_len;
 37     }
 38     Edge(){}
 39 };
 40 
 41 int n,m,q;
 42 int s[MAX_M];
 43 int t[MAX_M];
 44 int v[MAX_M];
 45 int par[MAX_N];
 46 int maxn[MAX_N][MAX_N];
 47 bool vis[MAX_N];
 48 vector<E> e;
 49 vector<Edge> edge[MAX_N];
 50 
 51 void read()
 52 {
 53     scanf("%d%d%d",&n,&m,&q);
 54     for(int i=1;i<=m;i++)
 55     {
 56         scanf("%d%d%d",&s[i],&t[i],&v[i]);
 57         e.push_back(E(s[i],t[i],v[i]));
 58     }
 59 }
 60 
 61 void init_union_find()
 62 {
 63     for(int i=1;i<=n;i++)
 64     {
 65         par[i]=i;
 66     }
 67 }
 68 
 69 int find(int x)
 70 {
 71     return par[x]==x ? x : par[x]=find(par[x]);
 72 }
 73 
 74 void unite(int x,int y)
 75 {
 76     int px=find(x);
 77     int py=find(y);
 78     if(px==py) return;
 79     par[px]=py;
 80 }
 81 
 82 bool same(int x,int y)
 83 {
 84     return find(x)==find(y);
 85 }
 86 
 87 int kruskal()
 88 {
 89     init_union_find();
 90     sort(e.begin(),e.end());
 91     int cnt=0;
 92     int res=0;
 93     for(int i=0;i<e.size();i++)
 94     {
 95         E temp=e[i];
 96         if(!same(temp.s,temp.t))
 97         {
 98             cnt++;
 99             res+=temp.len;
100             unite(temp.s,temp.t);
101             edge[temp.s].push_back(Edge(temp.t,temp.len));
102             edge[temp.t].push_back(Edge(temp.s,temp.len));
103         }
104     }
105     return cnt==n-1 ? res : -1;
106 }
107 
108 void dfs(int now)
109 {
110     vis[now]=true;
111     for(int i=0;i<edge[now].size();i++)
112     {
113         Edge temp=edge[now][i];
114         if(!vis[temp.dest])
115         {
116             for(int j=1;j<=n;j++)
117             {
118                 if(vis[j])
119                 {
120                     maxn[j][temp.dest]=maxn[temp.dest][j]=max(maxn[j][now],temp.len);
121                 }
122             }
123             dfs(temp.dest);
124         }
125     }
126 }
127 
128 void work()
129 {
130     kruskal();
131     memset(maxn,0,sizeof(maxn));
132     memset(vis,false,sizeof(vis));
133     dfs(1);
134     int i,x;
135     while(q--)
136     {
137         scanf("%d%d",&i,&x);
138         if(x<=maxn[s[i]][t[i]]) printf("Yes\n");
139         else printf("No\n");
140     }
141 }
142 
143 int main()
144 {
145     read();
146     work();
147 }

 

转载于:https://www.cnblogs.com/Leohh/p/8072682.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值