第三场 Two Famous Companies 最小生成树 + 二分

Two Famous Companies
时间限制: 1 Sec 内存限制: 128 MB

题目描述
In China, there are two companies offering the Internet service for the people from all cities: China Telecom and China Unicom. They both are planning to build cables between cities. Obviously, the government wants to connect all the cities in minimum costs. So the minister of finance Mr. B wants to choose some of the cable plans from the two companies and calculate the minimum cost needed to connect all the cities. Mr. B knows that N-1 cables should be built in order to connect all N cities of China. For some honorable reason, Mr. B should choose K cables from the China Telecom and the rest N-1-K cables from the China Unicom. Your job is to help Mr. B determine which cables should be built and the minimum cost to build them. You may assume that the solution always exists.
输入
Each test case starts with a line containing the number of cities N (1 <= N <= 50,000), number of cable plans M (N-1 <= M <= 100,000) and the number of required cables from China Telecom K (0 <= K <= N-1). This is followed by M lines, each containing four integers a, b, c, x (0 <= a, b <= N-1, a != b, 1 <= c <= 100, x in {0,1} indicating the pair of cities this cable will connect, the cost to build this cable and the company this cable plan belongs to. x=0 denotes that the cable plan belongs to China Telecom and x=1 denotes that the cable plan is from China Unicom.
输出
For each test case, display the case number and the minimum cost of the cable building.
样例输入 Copy
2 2 1
0 1 1 1
0 1 2 0
2 2 0
0 1 1 1
0 1 2 0
样例输出 Copy
Case 1: 2
Case 2: 1
提示
In the first case, there are two cable plans between the only two cities, one from China Telecom and one from China Unicom. Mr. B needs to choose the one from China Telecom to satisfy the problem requirement even the cost is higher.
In the second case, Mr. B must choose the cable from China Unicom, which leads the answer to 1.

一个下标写错引发的惨案。

题意:给你m条边,每个边属于a b两个公司的一个,a公司必须选k条边,让你求出满足条件且联通的最小代价。
比较容易看出来是最小生成树,但是怎么处理a公司选k条边呢?显然是不能跑一遍a公司直接选出来前k小的边,因为可能b公司有更好的边来替代。
那我们考虑一下两种情况:
( 1 ) k < = c n t (1) k <=cnt 说明a公司选多了
( 2 ) k > c n t (2) k>cnt 说明a公司选少了
对于选多了的情况,那说明a公司权值是比b公司小。而选少了说明a公司权值比b公司大。
对于这两种我们可以二分一个值,将这个值加到a公司的每条边上。第一种情况就是 l = mid + 1 ,第二种就是 r = mid -1 。

check里面需要改变权值,所以需要重新排序。这个复杂度是 O ( M l o g M l o g ( 200 ) ) O(MlogMlog(200)) 的,卡卡常其实也能过去 。不过可以将a b公司的提前分开,让后 O ( M ) O(M) 排序,这样复杂度就可以降低一个档次了。

卡常过的:

#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define Mid (tr[u].l+tr[u].r>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define pb push_back
#define mk make_pair
#define re register
using namespace std;

typedef long long LL;
typedef pair<int,int> PII;

const int N=200010,mod=1e9+7,INF=0x3f3f3f3f;
const double eps=1e-6;

int n,m,k;
int p[N];
LL sum;
struct Node
{
    int a,b,w,f;
    bool operator < (const Node &x) const
    {
        if(w!=x.w) return w<x.w;
        else return f<x.f;
    }
}edge[N*2];

template <class T>
bool read(T &ret)//输入
{
    char c;
    int sgn;
    T bit=0.1;
    if(c=getchar(), c==EOF)
        return 0;
    while(c!='-' && c!='.' && (c<'0' || c>'9'))
        c=getchar();
    sgn=(c=='-')? -1:1;
    ret=(c=='-')? 0:(c-'0');
    while(c=getchar(), c>='0' && c<='9')
        ret=ret*10+(c-'0');
    if(c==' ' || c=='\n')
    {
        ret*=sgn;
        return 1;
    }
    while(c=getchar(), c>='0' && c<='9')
        ret+=(c-'0')*bit, bit/=10;
    ret*=sgn;
    return 1;
}

inline void out(int x)//输出
{
    if(x>9)
        out(x/10);
    putchar(x%10+'0');
}

int find(int x)
{
    return x==p[x]? x:p[x]=find(p[x]);
}

void init()
{
    for(int i=0;i<=n;++i) p[i]=i;
}

int check(int mid)
{
    int cnt=0;
    for(re int i=1;i<=m;++i)
        if(edge[i].f==0) edge[i].w+=mid;
    sort(edge+1,edge+1+m);
    sum=0; init();
    for(re int i=1;i<=m;++i)
    {
        int a=edge[i].a,b=edge[i].b,c=edge[i].f,w=edge[i].w;
        int pa=find(a),pb=find(b);
        if(pa!=pb)
        {
            sum+=w;
            if(!c) cnt++;
            p[pa]=pb;
        }
    }
    for(re int i=1;i<=m;++i)
        if(edge[i].f==0) edge[i].w-=mid;
    return cnt>=k;
}

int main()
{
//  ios::sync_with_stdio(false);
//  cin.tie(0);

    int _=1;
    while(scanf("%d%d%d",&n,&m,&k)!=EOF)
    {
        for(re int i=1;i<=m;++i)
        {
            int a,b,c,d;
            read(a),read(b),read(c),read(d);
            a++,b++;
            edge[i]={a,b,c,d};
        }
        int l=-100,r=100,ans=0;
        while(l<=r)
        {
            int mid=l+r>>1;
            if(check(mid)) ans=sum-k*mid,l=mid+1;
            else r=mid-1;
        }
        printf("Case %d: %d\n",_++,ans);
    }



    return 0;
}
/*

*/



第二种:


#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define Mid (tr[u].l+tr[u].r>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define pb push_back
#define mk make_pair
using namespace std;

typedef long long LL;
typedef pair<int,int> PII;

const int N=200010,mod=1e9+7,INF=0x3f3f3f3f;
const double eps=1e-6;

int n,m,k;
int tot1,tot2,tot;
int p[N],sum;
struct Node
{
    int a,b,w,x;
    bool operator < (const Node &W) const
    {
        return w<W.w;
    }
}edge1[N],edge2[N],edge[N];

void init()
{
    for(int i=1;i<=n;i++) p[i]=i;
}

int find(int x)
{
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}

bool check(int x)
{
    int cnt=tot=sum=0; init();
    for(int i=1;i<=tot1;i++) edge1[i].w+=x;
    for(int i=1,j=1;i<=tot1||j<=tot2;)
    {
        int m1,m2; m1=m2=INF;
        if(i<=tot1) m1=edge1[i].w;
        if(j<=tot2) m2=edge2[j].w;
        if(m1<=m2) edge[++tot]=edge1[i++];
        else edge[++tot]=edge2[j++];
    }

    for(int i=1;i<=tot;i++)
    {
        int a=edge[i].a,b=edge[i].b,w=edge[i].w,x=edge[i].x;
        int pa=find(a),pb=find(b);
        if(pa!=pb)
        {
            if(!x) cnt++;
            p[pa]=pb;
            sum+=w;
        }
    }
    for(int i=1;i<=tot1;i++) edge1[i].w-=x;
    return cnt>=k;
}

int main()
{
//  ios::sync_with_stdio(false);
//  cin.tie(0);

    int _=1;
    while(scanf("%d%d%d",&n,&m,&k)!=EOF)
    {
        tot1=0,tot2=0;
        for(int i=1;i<=m;i++)
        {
            int a,b,c,x; scanf("%d%d%d%d",&a,&b,&c,&x);
            a++,b++;
            if(!x) edge1[++tot1]={a,b,c,x};
            else edge2[++tot2]={a,b,c,x};
        }
        sort(edge1+1,edge1+1+tot1);
        sort(edge2+1,edge2+1+tot2);
        int l=-100,r=100,ans=0;
        while(l<=r)
        {
            int mid=l+r>>1;
            if(check(mid)) ans=sum-k*mid,l=mid+1;
            else r=mid-1;
        }
        printf("Case %d: %d\n",_++,ans);
    }


    return 0;
}
/*

*/


猜你喜欢

转载自blog.csdn.net/DaNIelLAk/article/details/108444414