传送门:点击打开链接
题意:有N个城市,M条双向边,K条龙。
第i条龙在Ci城市,初始有Si个头,如果龙还活着,每一分钟增加Ci个头
一个勇士1分钟可以砍一个龙的头,也可以选择移动到当前城市周围相邻的城市去
不限定时间的前提下,至少要召集多少勇士,才能把所有的龙全部杀掉
勇士的初始位置可以是任意的
思路:这道题出的非常好,但是却一下子很难想。
首先,可以想到,这个图可能并不完全连通,那么就会有很多个连通分量,这些连通分量之间没有联系,那么勇士就不能跨连通分量转移,所以对于多个连通分量,肯定是要肯开处理,然后积累总和的。
其次,怎样才能将龙给杀掉呢?有两种办法。在第1秒的时候,就直接有Si个勇士,直接把初始血量打成0,就杀了。或者是,在整个连通分量中勇士的个数超过了Ni的前提下,肯定能将这条龙杀了,因为时间是无限的,我只要召集整个连通分量中的勇士,然后每分钟造成的伤害比回复快,那么肯定是可以的。
想到这里,我们就能想到了,之所以告诉你边,完全只是要你分别求出连通分量而已。。所以我们使用并查集将n个点分开成多个连通分量。
对于一个连通分量中,,到底应该派多少勇士呢。因为有两种决策,然后当时卡死在这里,,最初想利用贪心策略,但是想了很久都没有构造出来,后来发现如果去二分其实特别简单。那么如何二分呢。二分的对象是,对于这个连通分量中,至少需要m个勇士,才能将这个连通分量中的龙全部杀掉。那么对于Ni+1<=m的那些龙,肯定是可以杀死的,因为上面的策略2嘛。对于Ni+1>m的龙,如果想只用m个勇士就能把这条龙杀了的话,那么就必须需要Si个勇士,直接在第一分钟就杀了它。所以,二分中的验证函数,就是积累Ni+1>m的龙的Si之和,与m去做比较,看大小关系即可~
#include<map>
#include<set>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<string>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#define fuck printf("fuck")
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w+",stdout)
using namespace std;
typedef long long LL;
const int MX = 2000 + 5;
vector<int>G[MX];
int P[MX], A[MX];
struct Data {
int C, S, N;
Data() {}
Data(int _C, int _S, int _N) {
C = _C; S = _S; N = _N;
}
} D[MX];
int find(int x) {
return P[x] == x ? x : (P[x] = find(P[x]));
}
bool check(int id, int x) {
int sum = 0;
for(int i = 0; i < G[id].size(); i++) {
int v = G[id][i];
if(x < D[v].N + 1) sum += D[v].S;
}
return sum <= x;
}
int main() {
int n, m, k; //FIN;
while(~scanf("%d%d%d", &n, &m, &k), n) {
for(int i = 1; i <= n; i++) {
P[i] = i;
G[i].clear();
}
for(int i = 1; i <= m; i++) {
int u, v;
scanf("%d%d", &u, &v);
int p1 = find(u), p2 = find(v);
P[p1] = p2;
}
int rear = 0;
for(int i = 1; i <= n; i++) {
if(i == find(i)) A[++rear] = i;
}
for(int i = 1; i <= k; i++) {
int ci, si, ni;
scanf("%d%d%d", &D[i].C, &D[i].S, &D[i].N);
int p = find(D[i].C);
G[p].push_back(i);
}
int ans = 0;
for(int i = 1; i <= rear; i++) {
int l = 0, r = 1e9, m, ret;
while(l <= r) {
m = (l + r) >> 1;
if(check(A[i], m)) {
ret = m;
r = m - 1;
} else l = m + 1;
}
ans += ret;
}
printf("%d\n", ans);
}
return 0;
}