HDU - 7308 Operation Hope( 2023“钉耙编程”中国大学生算法设计超级联赛第三场 I)

4 篇文章 0 订阅

题目

Little Q \text{Little Q} Little Q is playing an RPG \text{RPG} RPG online game. In this game, there are n n n characters labeled by 1 , 2 , … , n 1,2,…,n 1,2,,n. The i-th character has three types of quotas:

  • a i a_i ai - The maximum points of damage he can achieve in 15 15 15 seconds.
  • b i b_i bi - The maximum points of damage he can achieve in 40 40 40 seconds.
  • c i c_i ci - The maximum points of damage he can achieve in 120 120 120 seconds.

You are the team leader working for the new balance between these n n n characters, aiming at bringing hope to the weak characters. For each character, your teammates have made a plan to strengthen some skills such that the three quotas may be increased as a result. Note that it is not allowed to weaken characters, because it will make their owners upset.

To make a perfect balance, you need to accept some plans and deny others such that the gap between all the n n n characters is minimized. Note that a plan can only be entirely accepted or entirely denied. Here, the gap is defined as:

max ⁡ ( max ⁡ 1 ≤ i ≤ n a i − min ⁡ 1 ≤ i ≤ n a i , max ⁡ 1 ≤ i ≤ n b i − min ⁡ 1 ≤ i ≤ n b , max ⁡ 1 ≤ i ≤ n c i − min ⁡ 1 ≤ i ≤ n c i ) \max (\max\limits_{1\leq i\leq n}a_i-\min\limits_{1\leq i\leq n} a_i,\max\limits_{1\leq i\leq n}b_i-\min\limits_{1\leq i\leq n} b,\max\limits_{1\leq i\leq n}c_i-\min\limits_{1\leq i\leq n} c_i) max(1inmaxai1inminai,1inmaxbi1inminb,1inmaxci1inminci)

题目大意

Q \text{Q} Q 正在玩一款 RPG \text{RPG} RPG(开放世界游戏)。

在这个游戏中,有 n n n 个角色被标记为 1 , 2 , ⋯   , n 1,2,\cdots,n 1,2,,n。第 i i i 个角色有三种配额类型:

  • a i a_i ai -他在15秒内所能达到的最大伤害。

  • b i b i bi -他在40秒内所能达到的最大伤害。

  • c i c i ci -他在120秒内所能达到的最大伤害。

你是(万恶)善良的策划,为这 n n n 个角色之间的新平衡而努力,旨在为弱势角色带来希望。对于每个角色,你的队友已经制定了一个计划来加强一些技能,这样三个配额可能会因此而增加。

注意,它不允许削弱角色,因为它会使他们的主人心烦意乱。

为了达到完美的平衡,你需要接受一些计划并拒绝其他计划,这样所有 n n n 个角色之间的差距就会最小化。

请注意,计划只能被完全接受或完全拒绝。

这里,差距定义为

max ⁡ ( max ⁡ 1 ≤ i ≤ n a i − min ⁡ 1 ≤ i ≤ n a i , max ⁡ 1 ≤ i ≤ n b i − min ⁡ 1 ≤ i ≤ n b , max ⁡ 1 ≤ i ≤ n c i − min ⁡ 1 ≤ i ≤ n c i ) \max (\max\limits_{1\leq i\leq n}a_i-\min\limits_{1\leq i\leq n} a_i,\max\limits_{1\leq i\leq n}b_i-\min\limits_{1\leq i\leq n} b,\max\limits_{1\leq i\leq n}c_i-\min\limits_{1\leq i\leq n} c_i) max(1inmaxai1inminai,1inmaxbi1inminb,1inmaxci1inminci)

思路

一眼 2-SAT \text{2-SAT} 2-SAT。(很明显,我不会)

难点在建图,我们发现对于这道题不是很好建图,于是我们曲线救国。

我们可以先二分答案,然后在判断二分的答案能否满足。

这时候我们建边就可以按如下方法建边:

  • 对于两个角色,枚举他们选的方案。
  • 如果他们选的方案会使得答案大于二分的值,则连边。( 2 − S A T 2-SAT 2SAT 的边,即一个方案和另一个枚举的方案的反方案)

然后就是一个 2 − S A T 2-SAT 2SAT 版题了。

代码

#include <bits/stdc++.h>
using namespace std;
int T, n, a[200005][5], ans, k, mid, cnt, tot, vis[200005], q[200005], f[200005];
struct node {
    int id[200005], h, t, d;
    int get(int val) {
        if (h <= t)
            if (a[id[h]][d] < val - mid) {
                h++;
                return id[h - 1];
            }
        if (h <= t)
            if (a[id[t]][d] > val + mid) {
                t--;
                return id[t + 1];
            }
        return 0;
    }
} A[5];
bool cmp(int x, int y) { return a[x][k] < a[y][k]; }
void dfs(int x) {
    if (vis[x])
        return;
    vis[x] = 1;
    for (int i = 1; i <= 3; i++)
        while (1) {
            int to = A[i].get(a[x][i]);
            if (!to)
                break;
            dfs(to <= n ? to + n : to - n);
        }
    q[++tot] = x;
}
void ddfs(int x) {
    if (!vis[x])
        return;
    vis[x] = 0, f[x] = cnt;
    for (int i = 1; i <= 3; i++)
        while (1) {
            int to = A[i].get(a[x <= n ? x + n : x - n][i]);
            if (!to)
                break;
            ddfs(to);
        }
}
bool check() {
    cnt = tot = 0;
    for (int i = 1; i <= n * 2; i++) vis[i] = 0;
    for (int i = 1; i <= 3; i++) A[i].h = 1, A[i].t = n * 2;
    for (int i = 1; i <= n * 2; i++)
        if (!vis[i])
            dfs(i);
    for (int i = 1; i <= 3; i++) A[i].h = 1, A[i].t = n * 2;
    for (int i = tot; i; i--)
        if (vis[q[i]])
            cnt++, ddfs(q[i]);
    for (int i = 1; i <= n; i++)
        if (f[i] == f[i + n])
            return 0;
    return 1;
}
int main() {
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            scanf("%d%d%d%d%d%d", &a[i][1], &a[i][2], &a[i][3], &a[i + n][1], &a[i + n][2], &a[i + n][3]);
        int minn = 1e9, maxn = -1e9;
        for (int i = 1; i <= n * 2; i++)
            for (int j = 1; j <= 3; j++) maxn = max(maxn, a[i][j]), minn = min(minn, a[i][j]);
        for (k = 1; k <= 3; k++) {
            for (int i = 1; i <= n * 2; i++) A[k].id[i] = i;
            A[k].h = 1, A[k].t = n * 2, A[k].d = k;
            sort(A[k].id + 1, A[k].id + n * 2 + 1, cmp);
        }
        int l = 0, r = maxn - minn;
        while (l <= r) {
            mid = (l + r) / 2;
            if (check())
                ans = mid, r = mid - 1;
            else
                l = mid + 1;
        }
        printf("%d\n", ans);
    }
    return 0;
}
  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值