【题解】P2798 爆弹虐场

不想看题面的同学可以看完翻译再去做题

题目大意:无向图有n个点,m条边,每条边有两个权值,分别称为T和t,先要从m条边中连点成连通图,要求图中权值最大的边权值尽量小,每条边所选择的权值是T[i]还是t[i]自由,但是要求使用T[i]的使用次数大于等于k次

虽然题面里说"时间很宝贵"之类的描述,但实际上并没有对所用边的权值和有什么区别,根据这道题的描述我们总结出以下几点

1. 二分答案,二分的是生成图中权值最大边的权值

2. 本题没有严谨要求一定要使用m-1条边,所以存在情况已经生成树后任然可以使用边来达到使用T[i]次数k次以上的要求

3. 同理于2,对任何符合条件的边我们都可以连起来,最后只需要判断图连通性即可

虽然翻了几篇代码都是直接当使用m-1条边来做的,但我重写代码却WA掉了一个点,我认为这是很重要的一点,这种特殊情况在本题中存在明显是合法的,需要去考虑

代码:

```

//2018/12/1->NHDR233->AtHM->luoguP2798
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define LL long long
using namespace std;
inline int read() {
    static int n, ch;
    n = 0; ch = getchar();
    while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)) n = n*10+ch-'0', ch = getchar();
    return n;
}
void smax(int& x, int y) {if (x < y) x = y;}
int n, m, lef, rig, num, father[10100];
struct node {
    int u, v, t1, s1;
} k[20200];
void init() {
    n = read(); num = read(); m = read();
    for (int i = 1; i <= m; ++i) {
        k[i].u = read();
        k[i].v = read();
        k[i].t1 = read();
        k[i].s1 = read();
        smax(rig, k[i].t1);
        smax(rig, k[i].s1);
    }
}
void setfather() {for (int i = 1; i <= n; ++i) father[i] = i;}
int find(int x) {
    if (father[x] != x) father[x] = find(father[x]);
    return father[x];
}
int main() {
    init();
    while (lef+1 < rig) {
        int cnt = 0, picnt = 0;
        int mid = (lef+rig)>>1;
        setfather();
        for (int i = 1; i <= m; ++i) {
            int u = find(k[i].u);
            int v = find(k[i].v);
            if (k[i].t1 <= mid) cnt++;
            if (u != v) {
                if (k[i].t1 <= mid or k[i].s1 <= mid) {
                    picnt++;
                    father[u] = v;
                }
            }
        }
        if (cnt < num or picnt < n-1) {
            lef = mid;
        }else {
            rig = mid; }
    }
    printf("%d", rig);
}

```

总结:二分是好想的,问题是是否构建出特殊数据

猜你喜欢

转载自www.cnblogs.com/NHDR233/p/11246686.html