不想看题面的同学可以看完翻译再去做题
题目大意:无向图有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);
}
```
总结:二分是好想的,问题是是否构建出特殊数据
转载于:https://www.cnblogs.com/NHDR233/p/11246686.html