Codeforces Round #709 ABC

 A. Prison Break

题意:有一个大小为a * b的网格,问至少拆除几条边可以使从任何一个格子都能出去

几个格子就拆几条边

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int N = 1e6 + 7;
const int M = 2e6 + 7;


int main() {
    int t, n, m;
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);
        cout << n * m << '\n';
    }
    return 0;
}

B. Restore Modulo(思维)

 

题意:

给定一个序列,问是否能找到一组m、c使得

(1)a_{1} = s % m

(2)a_{i} = (a_{i - 1} + c) % m (1<i\leq n)

若不存在,输出-1,否则求一组m最大的解,若m可以无限大输出0。

思路:

(1)若数列等差,m可以无限大(具体证明不会

(2)每一个a_{i}<a_{i + 1}的位置,a_{i + 1} - a_i 即为c,并且所有a_{i + 1} - a_i应该相同,否则不可能

(3)每一个a_i>a_{i + 1}的位置肯定受到了%m的影响,令 b = a_i - a_{i + 1},所有b应该相同,否则不可能

(4)b + c 即为m,c = (c + m) % m,判断一下此时m是否大于等于原序列中的最大值,若是则不可能。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int N = 1e5 + 7;
const int M = 2e6 + 7;
ll a[N];

int main() {
    int t, n;
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        bool f1 = 1, f2 = 0;
        ll c = -1, m = -1, maxx = -1;
        for(int i = 1; i <= n; ++i) {
            scanf("%lld", &a[i]);
            maxx = max(maxx, a[i]);
            if(f1 && i > 1 && a[i] != a[i - 1]) f1 = 0;
            if(!f2 && i > 1 && a[i] == a[i - 1]) f2 = 1;
        }
        if(f1) {
            printf("0\n");
            continue;
        }
        if(f2) {
            printf("-1\n");
            continue;
        }
        bool flag = 1;
        for(int i = 2; i <= n; ++i) {
            if(a[i] > a[i - 1]) {
                if(c == -1) c = a[i] - a[i - 1];
                else {
                    if(a[i] - a[i - 1] != c) {
                        flag = 0;
                        break;
                    }
                }
            }
        }
        if(!flag) {
            printf("-1\n");
            continue;
        }
        for(int i = 2; i <= n; ++i) {
            if(a[i] < a[i - 1]) {
                if(m == -1) m = a[i - 1] - a[i];
                else {
                    if(a[i - 1] - a[i] != m) {
                        flag = 0;
                        break;
                    }
                }
            }
        }
        if(!flag) {
            printf("-1\n");
            continue;
        }
        if(c == -1 || m == -1) {
            printf("0\n");
            continue;
        }
        m += c;
        c = (c + m) % m;
        if(maxx >= m) printf("-1\n");
        else printf("%lld %lld\n", m, c);
    }
    return 0;
}

C. Basic Diplomacy(贪心 || 网络流)

题意:有m天,每天都要选择一个人,现在给出每天可以选择的人有哪些,每个人不能被选超过 \left \lceil \frac{m}{2} \right \rceil 天,输出一种选法。

贪心:

最多只有一个人超过 \left \lceil \frac{m}{2} \right \rceil 天,可以先给每一天随机分配一个人,如果某个人的邀请次数超过限制条件,把这一天换一个人即可,最后判断是否合法。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int N = 1e5 + 7;
const int M = 2e6 + 7;

vector<int>mp[N];
int n, m, ans[N], cnt[N];

int main() {
    int t, k, a;
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= m; ++i) mp[i].clear();
        for(int i = 1; i <= n; ++i) cnt[i] = 0;
        for(int i = 1; i <= m; ++i) {
            scanf("%d", &k);
            for(int j = 1; j <= k; ++j) {
                scanf("%d", &a);
                mp[i].emplace_back(a);
            }
            ans[i] = a;
            cnt[a]++;
        }
        for(int i = 1; i <= m; ++i) {
            if(cnt[ans[i]] <= (m + 1) / 2) continue;
            int siz = mp[i].size();
            for(int j = 0; j < siz; ++j) {
                if(cnt[mp[i][j]] < (m + 1) / 2) {
                    cnt[ans[i]]--;
                    cnt[mp[i][j]]++;
                    ans[i] = mp[i][j];
                    break;
                }
            }
        }
        bool flag = 1;
        for(int i = 1; i <= n; ++i) {
            if(cnt[i] > (m + 1) / 2) {
                flag = 0;
                break;
            }
        }
        if(!flag) printf("NO\n");
        else {
            printf("YES\n");
            for(int i = 1; i <= m; ++i) {
                if(i > 1) printf(" ");
                printf("%d", ans[i]);
            }
            printf("\n");
        }
    }
    return 0;
}

网络流做法:

天数:1~m,人:m + 1~m + n

超级源点和每一天连边,流量为1

每天和当天可以选择的人连边,流量为1

每人和超级汇点连边,流量为 \left \lceil \frac{m}{2} \right \rceil

注意路径的输出

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int N = 2e5 + 7;
const int M = 2e6 + 7;

int head[N], dis[N], tot, ans[N];
struct Edge {
    int to, next, cap, flow;
}edge[M];

void init() {
    tot = 2;
    memset(head, -1, sizeof(head));
}

void addedge(int u, int v, int w, int rw = 0) {
    edge[tot].to = v;
    edge[tot].cap = w;
    edge[tot].flow = 0;
    edge[tot].next = head[u];
    head[u] = tot++;

    edge[tot].to = u;
    edge[tot].cap = rw;
    edge[tot].flow = 0;
    edge[tot].next = head[v];
    head[v] = tot++;
}

int Q[N];
int dep[N], cur[N], sta[N];
bool bfs(int s, int t, int n) {
    int fron = 0, tail = 0;
    memset(dep, -1, sizeof(dep[0]) * (n + 1));
    dep[s] = 0;
    Q[tail++] = s;
    while(fron < tail) {
        int u = Q[fron++];
        for(int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;
            if(edge[i].cap > edge[i].flow && dep[v] == -1) {
                dep[v] = dep[u] + 1;
                if(v == t) return true;
                Q[tail++] = v;
            }
        }
    }
    return false;
}

int dinic(int s, int t, int n) {
    int maxflow = 0;
    while(bfs(s, t, n)) {
        for(int i = 0; i <= n; ++i) cur[i] = head[i];
        int u = s, tail = 0;
        while(cur[s] != -1) {
            if(u == t) {
                int tp = inf;
                for(int i = tail - 1; i >= 0; --i)
                    tp = min(tp, edge[sta[i]].cap - edge[sta[i]].flow);
                maxflow += tp;
                for(int i = tail - 1; i >= 0; --i) {
                    edge[sta[i]].flow += tp;
                    edge[sta[i] ^ 1].flow -= tp;
                    if(edge[sta[i]].cap - edge[sta[i]].flow == 0)
                        tail = i;
                }
                u = edge[sta[tail] ^ 1].to;
            }
            else if(cur[u] != -1 && edge[cur[u]].cap > edge[cur[u]].flow && dep[u] + 1 == dep[edge[cur[u]].to]) {
                sta[tail++] = cur[u];
                u = edge[cur[u]].to;
            }
            else {
                while(u != s && cur[u] == -1)
                    u = edge[sta[--tail] ^ 1].to;
                cur[u] = edge[cur[u]].next;
            }
        }
    }
    return maxflow;
}

int main() {
    int t, n, m, a, k;
    scanf("%d", &t);
    while(t--) {
        init();
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= m; ++i) {
            scanf("%d", &k);
            for(int j = 1; j <= k; ++j) {
                scanf("%d", &a);
                addedge(i, a + m, 1);
            }
        }
        int s = n + m + 1, t = n + m + 2;
        for(int i = 1; i <= m; ++i) addedge(s, i, 1);
        for(int i = m + 1; i <= m + n; ++i) addedge(i, t, (m + 1) / 2);
        int maxflow = dinic(s, t, t);
        if(maxflow != m) {
            printf("NO\n");
            continue;
        }
        printf("YES\n");
        for(int i = 1; i <= m; ++i) {
            for(int j = head[i]; ~j; j = edge[j].next) {
                if(edge[j].to != i && edge[j].to != s && edge[j].to != t && edge[j].flow) {
                    if(edge[j].to > m) {
                        ans[i] = edge[j].to - m;
                        break;
                    }
                }
            }
        }
        for(int i = 1; i <= m; ++i) {
            if(i > 1) printf(" ");
            printf("%d", ans[i]);
        }
        printf("\n");
    }
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值