并查集+二分 hnu13433 Dragons

来源:互联网 发布:win7进入windows后黑屏 编辑:程序博客网 时间:2024/05/22 03:17

传送门:点击打开链接

题意:有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;}


0 0
原创粉丝点击