tree【WQS二分+MST】

题目链接——洛谷(精确涉及到了WQS二分)

BZOJ-2654(不推荐)


  个人不推荐做BZOJ2654的这道题,因为那道题可以水过去,不用WQS二分也是可以的,可以直接二分答案,显然是没有这个好的。

  先在这里讲一下什么是WQS二分吧,也是从网上看来的,一开始在做这道题的时候,想到的也是存在这种可能性,但是依然WA了几次:

  先说题意:给你一个N个点M条边无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有K条白色边的生成树。

  然后WQS二分:也就是在黑边与白边存在相等的时候,我们直接二分的话,是得不到答案的,这时候就是需要去进行处理了,就有可能去取了“实际价值”更大的白边,而没有去取现实价值更小的黑边;或者现实价值更大的黑边、“实际价值”更小的白边。

  举个例子,我们现在对所有白边(先不要管为什么“加一个数”)统一加上一个值,然后,(譬如说是“+(-5)”)出现值变成「1(0),1(0),1(0),1(0),1(1),1(1),1(1)」,但是我们只需要四条边,两条白边即可,我们会很自然的选择前4条边,但是出现了4条白边的情况;再看(现在到了“+(-4)”),出现值变成「2(0),2(0),2(0),2(0),1(1),1(1),1(1)」,此时我们会拿3条黑边和1条白边的形式了。

  遇到上面这个形式应该怎么办呢,当“+(-5)”的情况的时候,其实我们就是得到了我们想要的答案了,因为需要两条白边,所以我们4 - (2 * (-5)) = 14就是我们要的答案了。(这就是WQS二分的解决办法QAQ)

  其实在这种情况下,我们固定了白边(因为上限是确定的),然后黑边因为等同于此时的“假定”白边,所以,我们实际上做了一个用黑边代替白边的过程。

  那么,上面讲到“对所有白边统一加一个数”,这是为什么呢?

  可以看到,当对所有白边的边权-INF的时候,能取尽可能多的白边了,同理的,对所有白边的边权+INF的时候,取到的白边就会尽可能的少了,条件是限行的,所以,我们可以这样二分答案去满足条件。

一组测试样例:

6 8 3
0 1 2 1
0 2 10 0
1 3 4 1
1 4 30 0
2 5 4 0
1 2 4 0
3 4 5 1
4 5 5 1
ans:27

My Code:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxE = 1e5 + 7, maxN = 5e4 + 7;
int N, M, need, line[2] = {0}, root[maxN], MST_ans;
int fid(int x) { return x == root[x] ? x : root[x] = fid(root[x]); }
struct Eddge
{
    int u, v, w, add;
    Eddge(int a=0, int b=0, int c=0, int d=0):u(a), v(b), w(c), add(d) {}
    friend bool operator < (Eddge e1, Eddge e2) { return e1.w < e2.w; }
}E[2][maxE];
inline void init() { for(int i=0; i<=N; i++) root[i] = i; }
bool flag = false;
inline bool Kruskal()
{
    init(); MST_ans = 0;
    int i = 1, j = 1, bian = 0, u, v, white = 0;
    while(i <= line[0] || j <= line[1])
    {
        if(bian == N - 1) break;
        if(j > line[1] || (i <= line[0] && E[0][i].w + E[0][i].add <= E[1][j].w))
        {
            u = fid(E[0][i].u); v = fid(E[0][i].v);
            if(u != v)
            {
                MST_ans += E[0][i].w + E[0][i].add;
                root[u] = v;
                white++;
                bian++;
            }
            i++;
        }
        else
        {
            u = fid(E[1][j].u); v = fid(E[1][j].v);
            if(u != v)
            {
                MST_ans += E[1][j].w;
                root[u] = v;
                bian++;
            }
            j++;
        }
    }
    return white >= need;
}
int main()
{
    scanf("%d%d%d", &N, &M, &need);
    for(int i=1, u, v, w, op; i<=M; i++)
    {
        scanf("%d%d%d%d", &u, &v, &w, &op);
        E[op][++line[op]] = Eddge(u, v, w, 0);
    }
    sort(E[0] + 1, E[0] + line[0] + 1);
    sort(E[1] + 1, E[1] + line[1] + 1);
    int l = -100, r = 100, mid, ans = 0;
    while(l <= r)
    {
        mid = (l + r) / 2;
        for(int i=1; i<=line[0]; i++) E[0][i].add = mid;
        if(Kruskal())
        {
            l = mid + 1;
            ans = MST_ans - need * mid;
        }
        else
        {
            r = mid - 1;
        }
    }
    if(!flag) printf("%d\n", ans);
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值