Codeforces Global Round 6

好演啊,完蛋了,只会ABC。

关注的人不够多,假如有榜看的话估计可以发现D是简单题来的,多关注几个红名爷好了。

首先A就想复杂了一开始就应该往质因数分解那里想。

B看了图也想复杂了,观察一下很显然。

C看错题太演了,单行单列要小心一点。

D应该先看榜的,比赛不是乱做,应该看榜,前面一圈人秒杀D,D就不应该是复杂的图,又消环又干嘛的。

A - Competitive Programme

题意:给一个字符串,对它重新排列使得它是60的倍数。

题解:首先这个是A题,在CF中要坚信它是水题。60的倍数一定是10的倍数所以必须有至少一个0,先把没有0的去掉。然后去掉其中一个0。然后要求是6的倍数,3的倍数满足十进制数位和都是3,2的倍数要求以2结尾,分别判断就可以了。

char s[1005];
 
void test_case() {
    scanf("%s", s + 1);
    int l = strlen(s + 1);
    int sum = 0;
    for(int i = 1; i <= l; ++i)
        sum += s[i] - '0';
    /*不是3的倍数*/
    if(sum % 3 != 0) {
        puts("cyan");
        return;
    }
    /*150*/
    int cnt0 = 0, cnt2 = 0, cntn0 = 0;
    for(int i = 1; i <= l; ++i) {
        if(s[i] == '0')
            ++cnt0;
        else
            ++cntn0;
        if((s[i] - '0') % 2 == 0)
            ++cnt2;
    }
    /*全是0*/
    if(cntn0 == 0) {
        puts("red");
        return;
    }
    /*没有0*/
    if(cnt0 == 0) {
        puts("cyan");
        return;
    }
    /*有0,也有至少两个偶数,0提供1个2和5,另一个提供另一个2*/
    if(cnt2 >= 2) {
        puts("red");
        return;
    }
    puts("cyan");
    return;
}

B - Dice Tower

题意:给一个数,问他是不是可以由一个骰子塔的所有接触空气的面的数字构成。

题解:123456轮流朝下有[15,20]6种不同的结果。而骰子的对面的和永远都是7,所以骰子塔的中间不管怎么摆都是+14每层。

void test_case() {
    /*最小的肯定是12345=15*/
    /*12346=16*/
    /*12356=17*/
    /*12456=18*/
    /*13456=19*/
    /*23456=20*/
    /*然后必须加一层,加一层最小的,注意骰子对面都是7,所以没最小的,都是+14*/
    ll x;
    scanf("%lld", &x);
    if(x <= 14) {
        puts("NO");
        return;
    }
    x %= 14;
    if(x >= 1 && x <= 6) {
        puts("YES");
        return;
    }
    puts("NO");
    return;
}

C - Diverse Matrix

题意:往一个矩阵里面填数,使得每行每列的gcd都两两各不相同。且最大的gcd最小。

题解:一开始没看见“且最大的gcd最小”,就随便掏了一群质数瞎乘上去。改了之后就发现其实是要用连续的自然数,首先给第一行安排一堆连续的自然数那么他们的gcd肯定就是1,然后在下面给他们一起乘上多少倍他们的gcd就是多少。注意只有1或1列的情况不能出现数字1,其他的情况是可以出现的。无论如何都是刚好从1开始到r+c的。

int a[505][505];
 
void test_case() {
    int r, c;
    scanf("%d%d", &r, &c);
    if(r == 1 && c == 1) {
        puts("0");
        return;
    }
    if(r == 1) {
        for(int j = 1; j <= c; ++j)
            a[1][j] = j + 1;
        for(int i = 1; i <= r; ++i) {
            for(int j = 1; j <= c; ++j)
                printf("%d%c", a[i][j], " 
"[j == c]);
        }
        return;
    }
    if(c == 1) {
        for(int i = 1; i <= r; ++i)
            a[i][1] = i + 1;
        for(int i = 1; i <= r; ++i) {
            for(int j = 1; j <= c; ++j)
                printf("%d%c", a[i][j], " 
"[j == c]);
        }
        return;
    }
    for(int i = 1; i <= r; ++i) {
        for(int j = 1; j <= c; ++j)
            a[i][j] = 1;
    }
    int curp = 0;
    for(int i = 1; i <= r; ++i) {
        int P = ++curp;
        for(int j = 1; j <= c; ++j)
            a[i][j] *= P;
    }
 
    for(int j = 1; j <= c; ++j) {
        int P = ++curp;
        for(int i = 1; i <= r; ++i)
            a[i][j] *= P;
    }
 
    for(int i = 1; i <= r; ++i) {
        for(int j = 1; j <= c; ++j)
            printf("%d%c", a[i][j], " 
"[j == c]);
    }
    return;
}

D - Decreasing Debts

题意:有n个人,m条借钱关系,你可以按照常识来简化借钱关系进行等价变换,使得总的借钱数额最小。

题解:消环DAG确实可以,这波亏一半,写起来有一些细节错了,没事下次就可以了。其实看分值这题就不应该这么做。这样写的细节难度至少有C的3倍。

vector<pii> PG1[200005];

void AddEdge1(int u, int v, int w) {
    if(u == v || w == 0)
        return;
    if(!PG1[v].empty()) {
        pii tmp = PG1[v].back();
        PG1[v].pop_back();
        int t = tmp.second;
        if(t == u) {
            if(w >= tmp.first) {
                AddEdge1(u, v, w - tmp.first);
                return;
            } else {
                AddEdge1(v, u, tmp.first - w);
                return;
            }
        }
        if(w >= tmp.first) {
            AddEdge1(u, t, tmp.first);
            AddEdge1(u, v, w - tmp.first);
            return;
        } else {
            AddEdge1(u, t, w);
            AddEdge1(v, t, tmp.first - w);
            return;
        }
    }
    if(w)
        PG1[u].push_back({w, v});
}

vector<pii> PG2[200005];

void AddEdge2(int u, int v, int w) {
    if(u == v || w == 0)
        return;
    if(!PG2[v].empty()) {
        pii tmp = PG2[v].back();
        PG2[v].pop_back();
        int t = tmp.second;
        if(t == u) {
            if(w >= tmp.first) {
                AddEdge2(u, v, w - tmp.first);
                return;
            } else {
                AddEdge2(v, u, tmp.first - w);
                return;
            }
        }
        if(w >= tmp.first) {
            AddEdge2(u, t, tmp.first);
            AddEdge2(u, v, w - tmp.first);
            return;
        } else {
            AddEdge2(u, t, w);
            AddEdge2(v, t, tmp.first - w);
            return;
        }
    }
    if(w)
        PG2[u].push_back({w, v});
}

vector<pii> G[200005];
vector<int> AG[200005];
queue<int> Q;

int outdeg[200005];

map<int, ll> MG[200005];

void test_case() {
    int n, m;
    scanf("%d%d", &n, &m);
    while(m--) {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        AddEdge1(u, v, w);
    }
    for(int i = 1; i <= n; ++i) {
        while(!PG1[i].empty()) {
            pii tmp = PG1[i].back();
            PG1[i].pop_back();
            int v = tmp.second, w = tmp.first;
            G[i].push_back({v, w});
            AG[v].push_back(i);
        }
    }
    for(int i = 1; i <= n; ++i) {
        outdeg[i] = G[i].size();
        if(outdeg[i] == 0)
            Q.push(i);
    }
    while(!Q.empty()) {
        int u = Q.front();
        Q.pop();
        for(auto &e : G[u])
            AddEdge2(u, e.first, e.second);
        for(auto &v : AG[u]) {
            --outdeg[v];
            if(outdeg[v] == 0)
                Q.push(v);
        }
    }
    for(int i = 1; i <= n; ++i) {
        while(!PG2[i].empty()) {
            pii tmp = PG2[i].back();
            PG2[i].pop_back();
            int u = i, w = tmp.first, v = tmp.second;
            MG[u][v] += w;
        }
    }
    int sum = 0;
    for(int i = 1; i <= n; ++i)
        sum += MG[i].size();
    printf("%d
", sum);
    for(int i = 1; i <= n; ++i) {
        for(auto &e : MG[i])
            printf("%d %d %lld
", i, e.first, e.second);
    }
}

这东西的复杂度怎么证明啊?会不会本身就是一个假算法?

题解:事实上每个人只需要关心他实际借出/借入了多少钱,对象是无所谓的,随便找个人填上去就可以了。

ll sum[200005];
vector<int>P, N;

map<int, ll> MG[200005];

void test_case() {
    int n, m;
    scanf("%d%d", &n, &m);
    while(m--) {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        sum[u] += w;
        sum[v] -= w;
    }
    for(int i = 1; i <= n; ++i) {
        if(sum[i] < 0)
            N.push_back(i);
        else if(sum[i] > 0)
            P.push_back(i);
    }

    while(!P.empty()) {
        int u = P.back();
        P.pop_back();
        int v = N.back();
        N.pop_back();
        ll d = min(sum[u], -sum[v]);
        sum[u] -= d;
        sum[v] += d;
        MG[u][v] += d;
        if(sum[u])
            P.push_back(u);
        if(sum[v])
            N.push_back(v);
    }

    int sum = 0;
    for(int i = 1; i <= n; ++i)
        sum += MG[i].size();
    printf("%d
", sum);
    for(int i = 1; i <= n; ++i) {
        for(auto &e : MG[i])
            printf("%d %d %lld
", i, e.first, e.second);
    }
}

E - Spaceship Solitaire

题意:建造一艘太空船,需要n种资源,第i种资源的需求是ai。每次加入/改变/删除一个任务奖励,达成该任务就获得一个奖励的资源,在每次操作后输出当前需要的资源数,注意每种资源的每个进度的奖励资源至多只有1个。

题解:一开始就是ai的和,然后加入一个必定会减少1。再继续加入的话要判断当前奖励的是否已经饱和,饱和的话就不再改变。反过来想的确是显然的,但是正向怎么构造呢?

int n, q, a[200005], b[200005];
map<int, int> M[200005];
ll sum;

void Sub(int u) {
    if(u) {
        --b[u];
        if(b[u] < a[u])
            ++sum;
    }
}

void Add(int u) {
    if(u) {
        ++b[u];
        if(b[u] <= a[u])
            --sum;
    }
}

void test_case() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        sum += a[i];
    }
    scanf("%d", &q);
    while(q--) {
        int s, t, u;
        scanf("%d%d%d", &s, &t, &u);
        Sub(M[s][t]);
        Add(u);
        M[s][t] = u;
        printf("%lld
", sum);
    }
}
原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12057840.html