CodeForces - 76A:Gift (最小生成树 解决单调性问题是思想)

题意:给定N点M边的无向连通图,每条边有两个权值(g,s)。 给定G,S。 让你给出一组(g0,s0)使得图中仅留下g<=g0, s<=s0的边之后,依然连通,并求Gg0+Ss0的最小值。 n<=200,m<=50000。

思路:枚举g0,求最小的s0,满足生成MST。 把边按g排序,一条边一条边的加入,然后在加入边的集合里面找出最小的s。但是每次排序复杂度过高,而且边数也过多,(LCT做也行吧,就不需要考虑这么多)。 我们去优化暴力的做法。假设新加入一条边,显然最多改变一条边,那么我们维护一个有序序列,表示MST用的边(N-1条),新加入后,手动排序(O(N)),然后把这N条边建立新的MST,就可以了。

#include<bits/stdc++.h>
#define ll long long
#define pair<ll,ll> pii
#define x first
#define y second
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=200010;
struct in{
    int u,v;ll a,b;
}s[maxn];
bool cmp(in p,in q){
    if(p.a!=q.a) return p.a<q.a;
    return p.b<q.b;
}
int fa[maxn],q[maxn];
int find(int x){
    if(x==fa[x]) return x;
    return fa[x]=find(fa[x]);
}
int main()
{
    int N,M,tot=0;  ll G,S,A=0,B=0,ans=-1;
    scanf("%d%d%lld%lld",&N,&M,&G,&S);
    rep(i,1,M){
        scanf("%d%d%lld%lld",&s[i].u,&s[i].v,&s[i].a,&s[i].b);
        if(s[i].u==s[i].v) i--,M--;
    }
    sort(s+1,s+M+1,cmp);
    rep(i,1,M){
        q[++tot]=i; int t=tot;
        while(t>1&&s[q[t]].b<s[q[t-1]].b) swap(q[t],q[t-1]),t--;
        rep(j,1,N) fa[j]=j; tot=0;
        rep(j,1,N) {
            int fu=find(s[q[j]].u);
            int fv=find(s[q[j]].v);
            if(fu==fv) continue;
            fa[fu]=fv;
            q[++tot]=q[j];
            if(tot==N-1) break;
        }
        if(tot==N-1){
            if(ans==-1) ans=G*s[i].a+S*s[q[N-1]].b;
            else ans=min(ans,G*s[i].a+S*s[q[N-1]].b);
        }
    }
    printf("%lld\n",ans);
    return 0;
}

 

猜你喜欢

转载自www.cnblogs.com/hua-dong/p/11184981.html
0条评论
添加一条新回复