【bzoj2654】 tree

http://www.lydsy.com/JudgeOnline/problem.php?id=2654 (题目链接)

今天考试题,以为是神题不可做,直接放弃了。。没想到这么水。。

题意:给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。题目保证有解。

solution 
  我们考虑把白边的权值增加,因为无论白边的权值增加多少,最小生成树中的白边不会改变。所以我们二分每次把所有白边的权值增加多少,按边权大小排序后克鲁斯卡尔看选出的白边是否大于need。统计答案。

代码:

// bzoj2654
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 2147483640
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;

const int maxn=1000010;
struct edge {
    int u,v,w,c;
    friend bool operator < (const edge &a,const edge &b) {
        return a.w==b.w?a.c<b.c:a.w<b.w;
    }
}e[maxn];
LL fa[maxn],u[maxn],v[maxn],w[maxn],c[maxn],tot,cnt,n,m,ned,sumv;

int find(int x) {
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
bool check(int x) {
    tot=cnt=0;
    for (int i=1;i<=n;i++) fa[i]=i;
    for (int i=1;i<=m;i++) {
        e[i].u=u[i],e[i].v=v[i],e[i].w=w[i],e[i].c=c[i];
        if (!c[i]) e[i].w+=x;
    }
    sort(e+1,e+m+1);
    for (int i=1;i<=m;i++) {
        int p=find(e[i].u),q=find(e[i].v);
        if (p!=q) {
            fa[p]=q;
            tot+=e[i].w;
            if (!e[i].c) cnt++;
        }
    }
    return cnt>=ned;
}
int main() {
    scanf("%lld%lld%lld",&n,&m,&ned);
    for (int i=1;i<=m;i++) {
        scanf("%lld%lld%lld%lld",&u[i],&v[i],&w[i],&c[i]);
        u[i]++;v[i]++;
    }
    int l=-10005,r=10005;
    while (l<=r) {
        int mid=(l+r)>>1;
        if (check(mid)) l=mid+1,sumv=tot-ned*mid;
        else r=mid-1;
    }
    printf("%lld",sumv);
    return 0;
}

  

时间: 2024-08-26 23:42:56

【bzoj2654】 tree的相关文章

【BZOJ2654】tree 二分+最小生成树

[BZOJ2654]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

【bzoj2654】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 1 1 0 1 2 0

二分+最小生成树【bzoj2654】: tree

2654: tree 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有need条白色边的生成树. 题目保证有解. 二分答案,然后跑最小生成树判断. 注意优先跑白色边. code: #include <iostream> #include <cstdio> #include <algorithm> using namespace std; const int wx=500017; inline int read(){ int sum=0,f=1; ch

【bzoj2654]】tree

给白色边都加上一个值,二分这个值,使得选取的白边数量减少 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> using namespace std; typedef long long LL; #define N 100010 struct Node { int x,y,

【BZOJ4987】Tree 树形DP

[BZOJ4987]Tree Description 从前有棵树. 找出K个点A1,A2,…,Ak. 使得∑dis(AiAi+1),(1<=i<=K-1)最小. Input 第一行两个正整数n,k,表示数的顶点数和需要选出的点个数. 接下来n-l行每行3个非负整数x,y,z,表示从存在一条从x到y权值为z的边. I<=k<=n. l<x,y<=n 1<=z<=10^5 n <= 3000 Output 一行一个整数,表示最小的距离和. Sample I

【POJ3237】Tree 树链剖分+线段树

[POJ3237]Tree Description You are given a tree with N nodes. The tree's nodes are numbered 1 through N and its edges are numbered 1 through N ? 1. Each edge is associated with a weight. Then you are to execute a series of instructions on the tree. Th

【Luogu1501】Tree(Link-Cut Tree)

[Luogu1501]Tree(Link-Cut Tree) 题面 洛谷 题解 \(LCT\)版子题 看到了顺手敲一下而已 注意一下,别乘爆了 #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<set> #include<map>

【HDU5909】Tree Cutting(FWT)

[HDU5909]Tree Cutting(FWT) 题面 vjudge 题目大意: 给你一棵\(n\)个节点的树,每个节点都有一个小于\(m\)的权值 定义一棵子树的权值为所有节点的异或和,问权值为\(0..m-1\)的所有子树的个数 题解 考虑\(dp\) 设\(f[i][j]\)表示以\(i\)为根节点的子树中,异或和为\(j\)的子树的个数 很显然,每次合并就是两个\(dp\)值做\(xor\)卷积 那么直接用\(FWT\)优化就行了 #include<iostream> #inclu

点分治【bzoj1468】 Tree

点分治[bzoj1468] Tree Description 给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K Input N(n<=40000) 接下来n-1行边描述管道,按照题目中写的输入 接下来是k Output 一行,有多少对点之间的距离小于等于k 点分治开始入门. 点分治,主要是解决形如:给你一棵树,求树上满足XX条件的点对的对数. 所以说应对的问题很多时候都和树形DP相同. 首先告诉自己,分治是高效的算法. 想一下,平时在面对普通的分治问题,每次肯定都是