BZOJ 2654 & 玄学二分+MST

题意:

  给一张图,边带权且带颜色黑白,求出一棵至少包含k条白边的MST

SOL:

  正常人都想优先加黑边或者是白边,我也是这么想的...你看先用白边搞一棵k条边的MST...然后维护比较黑边跟白边像堆一样慢慢往里面加...不过讲课的时候跟原题有点出入...这里只有k条边好像比较难维护...

  正解也非常巧妙...首先如果有一棵MST,他所含白边数量多于k,那么我们如果可以适当增加白边的边权那么我们就可以减少它的边而且达到最优....想想很有道理你让我证明那有点日了狗了...

  然后我们就二分白边的增加值...然后一遍一遍地跑kruscal...多一个log...感觉还是很神...

  反正我自己想我肯定想不出来...

Code:

  

/*=================================================================
# Created time: 2016-03-30 19:36
# Filename: 2654.cpp
# Description:
=================================================================*/
#define me AcrossTheSky
#include <cstdio>
#include <cmath>
#include <ctime>
#include <string>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm> 

#include <set>
#include <map>
#include <stack>
#include <queue>
#include <vector> 

#define lowbit(x) (x)&(-x)
#define FOR(i,a,b) for((i)=(a);(i)<=(b);(i)++)
#define FORP(i,a,b) for(int i=(a);i<=(b);i++)
#define FORM(i,a,b) for(int i=(a);i>=(b);i--)
#define ls(a,b) (((a)+(b)) << 1)
#define rs(a,b) (((a)+(b)) >> 1)
#define getlc(a) ch[(a)][0]
#define getrc(a) ch[(a)][1] 

#define maxn 1100
#define maxk 1010
#define maxm 100000
#define pi 3.1415926535898
#define _e 2.718281828459
#define INF 1070000000
using namespace std;
typedef unsigned long long ull; 

template<class T> inline
void read(T& num) {
    bool start=false,neg=false;
    char c;
    num=0;
    while((c=getchar())!=EOF) {
        if(c==‘-‘) start=neg=true;
        else if(c>=‘0‘ && c<=‘9‘) {
            start=true;
            num=num*10+c-‘0‘;
        } else if(start) break;
    }
    if(neg) num=-num;
}
/*==================split line==================*/
struct edge{
	int x,y,z,color;
}e[100010];
struct bian{
	int x,y,z,mrk;
}a[100010];
inline bool operator<(const bian &a,const bian &b){
	return a.z<b.z||a.z==b.z&&a.mrk>b.mrk;
}
int n,m,k,ans,tot,sum,tt;
int fa[100010];
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void judge(int mid)
{
    tot=sum=0;
    FORP(i,1,n) fa[i]=i;
    FORP(i,1,m){
        a[i].x=e[i].x;
        a[i].y=e[i].y;
        a[i].z=e[i].z+(e[i].color*mid);
        a[i].mrk=e[i].color;
    }
    sort(a+1,a+m+1);
    FORP(i,1,m){
        int fx=find(a[i].x),fy=find(a[i].y);
        if (fx==fy)continue;
        if (a[i].mrk)tot++;
        fa[fx]=fy;
        sum+=a[i].z;
    }
}
int main(){
    read(n); read(m); read(k);
    FORP(i,1,m){
        read(e[i].x); e[i].x++;
        read(e[i].y); e[i].y++;
        read(e[i].z);
        read(e[i].color); e[i].color^=1;
        if (e[i].color)tt++;
    }
    int l=-105,r=105;
    while (l<=r){
        int mid=(l+r)>>1;
        judge(mid);
        if (tot>=k){ans=sum-k*mid;l=mid+1;}
        else r=mid-1;
    }
    printf("%d\n",ans);
    return 0;
}
时间: 2024-12-05 10:54:30

BZOJ 2654 & 玄学二分+MST的相关文章

BZOJ 2654: tree( 二分 + MST )

我们给白色的边增加权值 , 则选到的白色边就会变多 , 因此可以二分一下. 不过这道题有点小坑... ------------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #define rep( i

BZOJ 2654 tree 二分+最小生成树

题目大意 给出一些边,每个边有一个边权和颜色.现在要求出最小边权有need个白边的生成树.输出这个边权. 思路 在白边上加一个权值,这样就可以人为的改变白边出现在最小生成树.这个东西显然可以二分.之后取一下最小值就可以了. CODE #define _CRT_SECURE_NO_WARNINGS #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #def

(MST+二分) bzoj 2654

2654: tree Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 515  Solved: 195[Submit][Status][Discuss] Description 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有need条白色边的生成树. 题目保证有解. Input 第一行V,E,need分别表示点数,边数和需要的白色边数. 接下来E行 每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑

BZOJ 2654 tree(二分答案+并查集)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2654 [题目大意] 给你一个无向带权连通图,每条边是黑色或白色. 让你求一棵最小权的恰好有need条白色边的生成树.题目保证有解. [题解] 我们发现对于选中的边白色是从小到大的,黑色也是从小到大的, 因此我们对所有的白色边加一个权值,那么排序后做mst选取的白色边数量增减性单调, 对于增加的权值进行二分,验证能否满足要求即可. [代码] #include <cstdio> #in

【BZOJ 2654】 MST

2654: tree Description 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有need条白色边的生成树. 题目保证有解. Input 第一行V,E,need分别表示点数,边数和需要的白色边数. 接下来E行,每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色). Output 一行表示所求生成树的边权和. V<=50000,E<=100000,所有数据边权为[1,100]中的正整数. Sample Input 2 2 1 0 1

bzoj 2654 tree - 二分法 - 最小生成树

给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有need条白色边的生成树. 题目保证有解. Input 第一行V,E,need分别表示点数,边数和需要的白色边数. 接下来E行,每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色). Output 一行表示所求生成树的边权和. V<=50000,E<=100000,所有数据边权为[1,100]中的正整数. Sample Input 2 2 1 0 1 1 1 0 1 2 0 Sample Outp

hdu4253 Two Famous Companies --- 二分+MST

给n个点,m条边的图,每条边要么属于a公司,要么属于b公司.要求一颗最小生成树,条件是其中属于a公司的边数为k. 这题做法很巧妙. 要求最小生成树,但有一定限制,搜索.贪心显然都不对. 要是能找到一种合理的控制方法,使得求MST的过程中可以控制a公司边的数量,那样问题就解决了. 所以我们可以人为给a公司的边加上一定的权值,使得其中一些边不得不退出MST的选择范围内. 如果此时求的mst里a公司的边数>k,那么就要增加权值:边数<k时,权值为负. 所以,通过二分边权值,可以使得求得mst里所含a

hdu4253 二分+MST (经典模型)

n个点,m条边,边分为A,B两类,要构造一棵最小生成树,且树中A边数量为k. 我们可以通过给所有A边加上权值dx来控制树中A边的数量.显然,当dx增大,A边数量kk会减少. 二分dx, 当kk>=k,增大dx(即l=mid+1),同时更新ans=sum(mst)-mid*k; 当kk<k,减小dx(即r=mid-1). 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using n

bzoj 1014 LCP 二分 Hash 匹配

求同一字符串的两个后缀的最长公共前缀. 将字符串按位置放到Splay中维护(每个节点还维护一下该子树的hash),然后二分前缀的长度,用splay计算出指定范围的hash,按hash是否相等来判断是否相同. 一开始是将字符串看成26进制,加上unsigned long long的自然溢出来计算哈希,但这样Wa掉了,改成27进制就AC了,但我还不知道为什么,望明者相告,谢. 1 /***********************************************************