【思维】Codeforces Round #709 (Div. 2) C. Basic Diplomacy

思维

C. Basic Diplomacy

题目大意

n n n 个人,给出 m m m 天每天可以选的人。现在每天要选一个人,问能否构造一组答案,使得没有人被选的次数大于 ⌈ m 2 ⌉ \lceil \frac m 2 \rceil 2m

解题思路

竟然是很简单的思维题,分类一下就行了

我们只需要考虑只能选他一人的天数大于 ⌈ m 2 ⌉ \lceil \frac m 2 \rceil 2m 的人的情况。

即:设有一人编号是 i i i,他可以被选的次数为 c n t i cnt_i cnti,如果找不到一个 c n t i cnt_i cnti 满足 c n t i > ⌈ m 2 ⌉ cnt_i \gt \lceil \frac m 2 \rceil cnti>2m,说明我们不管怎么选,最后都不会超,都满足要求。

如果存在 c n t i > ⌈ m 2 ⌉ cnt_i \gt \lceil \frac m 2 \rceil cnti>2m,按照如下步骤考虑:

i i i 必须被选的天数为 c n t x cnt_x cntx

1.判断 c n t x cnt_x cntx 是否大于 ⌈ m 2 ⌉ \lceil \frac m 2 \rceil 2m。如果大于,说明肯定不符合要求。

2.反之,若 c n t x ≤ ⌈ m 2 ⌉ cnt_x \le \lceil \frac m 2 \rceil cntx2m,则把 ⌈ m 2 ⌉ − c n t x \lceil \frac m 2 \rceil - cnt_x 2mcntx 的都分给 i i i。这样就满足了不会有别的人,被选的次数超过 ⌈ m 2 ⌉ \lceil \frac m 2 \rceil 2m 。由 c n t x ≤ ⌈ m 2 ⌉ cnt_x \le \lceil \frac m 2 \rceil cntx2m 可知 ⌈ m 2 ⌉ − c n t x ≥ 0 \lceil \frac m 2 \rceil - cnt_x \ge 0 2mcntx0,所以这个操作是可以实现的~

参考代码

#include<stdio.h>
#include<iostream>
#include<vector>
#include<cstring>
#include<cstdio>
#include<climits>
#include<cmath>
#include<algorithm>
#include<queue>
#include<deque>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<stack>
//#define LOCAL  //提交时一定注释
#define VI vector<int>
#define eps 1e-6
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
using namespace std;
typedef long long LL;
typedef double db;
const int inf = 0x3f3f3f3f;
const LL INF = 1e18;
const int N = 1e5 + 10;
#define ls rt << 1
#define rs rt << 1 | 1
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
inline int readint() {int x; scanf("%d", &x); return x;}

int cnt[N], res[N];
set<int> day[N];
int n, m;
void init() {
    for(int i = 1; i <= m; ++i) {
        day[i].clear();
    }
    memset(cnt, 0, sizeof(cnt));
    memset(res, 0, sizeof(res));
}

int main() {
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
//   freopen("output.txt", "w", stdout);
#endif
    int t = readint();
    while (t--) {
        init();
        n = readint(), m = readint();
        for(int i = 1; i <= m; ++i) {
            int k = readint();
            while (k--) {
                int p = readint();
                day[i].insert(p);
                cnt[p]++;
            }
        }
        int ma = 0, lim = (m + 1) / 2;
        for(int i = 1; i <= n; ++i) {
            if (cnt[i] > lim) {
                ma = i; break;
            }
        }
        if (ma == 0) {
            printf("YES\n");
            for(int i = 1; i <= m; ++i) {
                auto it = day[i].begin();
                printf("%d%s", *it, i == m ? "\n" : " ");
            }
            continue;
        }
        int num = 0;

        for(int i = 1; i <= m; ++i) {
            if (res[i]) continue;
            if (day[i].size() == 1 && day[i].find(ma) != day[i].end()) {
                res[i] = ma;
                num++;
            }
        }
        if (num > lim) {
            printf("NO\n"); continue;
        }
        for(int i = 1; i <= m; ++i) {
            if (res[i]) continue;
            if (day[i].find(ma) != day[i].end() && num < lim) {
                res[i] = ma;
                num++;
                continue;
            }

            for(auto it = day[i].begin(); it != day[i].end(); ++it) {
                int now = *it;
                if (now != ma) {
                    res[i] = now;
                    break;
                }
            }
        }
        printf("YES\n");
        for(int i = 1; i <= m; ++i) {
            printf("%d%s", res[i], i == m ? "\n" : " ");
        }
    }
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值