Codeforces Round #709 (div.2) A B C

第7分钟过A,第48分钟过B,然后憋到结束也没出C.
惨啊.

A

这是我在CF上见到的实现起来最简单的题目了.
会发现,想要达成"任意一个格子都有逃离的路线"意味着不能存在某一区域被墙圈住,如果把墙看作边,把格子的顶点看作节点,那么就转化为最终的(无向)图中不能存在环.
给定了一定数量的节点,要求在其中添加尽可能多的边,并且不能存在环.这显然会形成树,边数即为节点数-1.
之后只需要根据a,b计算出原有墙数x,节点数y,输出x-(y-1)即可.发现计算结果即为a*b.

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

int a, b;

void solve(){
	scanf("%d%d", &a, &b);
	printf("%d
", a * b);
}

int main(){
	int t;
	scanf("%d", &t);
	while(t--) solve();   

	return 0;
}

B

先推导一波观察一下,a[1]=s%m,a[2]=(a[1]+c)%=(s%m+c)%m=(s+c)%m,可知a[i]=(s+(i-1)c)%m.
会看出来,对于任意一个存在解的数组,a[i]-a[i-1]的值域只有两个或者一个值.其中,只有一个值的情况对应的数组为等差数列,这是m显然可取正无穷.
而对于值域有两个值的情况下:
1 9 17 6 14 3
a[i]-a[i-1]:8 8 -11 8 -11
发现解即为m=8-(-11)=19,c=8.这个结论具有普遍性.

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

int n, a[100010];

void solve() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", a + i);
    if (n <= 2) {
        puts("0");
        return;
    }

    bool b1 = false, b2 = false;
    int v1, v2;
    int big = 0;
    for (int i = 1; i <= n; i++) {
        big = max(big, a[i]);
        if (i == 1) continue;
        if (!b1) {
            b1 = true, v1 = a[i] - a[i - 1];
        } else if (!b2) {
            b2 = true, v2 = a[i] - a[i - 1];
        } else if (a[i] - a[i - 1] != v1 && a[i] - a[i - 1] != v2) {
            if (v1 == v2)
                v2 = a[i] - a[i - 1];
            else {
                puts("-1");
                // printf("!!!%d %d
", v1, v2);
                return;
            }
        }
    }

    if (v1 > v2) swap(v1, v2);
    if (v1 == v2)
        puts("0");
    else if (v2 - v1 > big)
        printf("%d %d
", v2 - v1, v2);
    else
        puts("-1");
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) solve();

    return 0;
}

实际上这显然是一个适合使用unordered_set的场景,参考这篇补题.

C

思维力不够,赛时没做出来.一直没有注意到m/2含有的特殊意义.
一旦有某人出现了m/2次后,剩余的人就不可能出现m/2次了.这是突破口.
并且显然地,如果发现某人可选的天数不超过m/2,可以直接让他出席其中所有闲置的天.
得到如下流程:
依次检查每个人的出现次数是否超过m/2
  否-使其出席所有可出席天
  是-检查有多少天只有他一人可出席
    不超过m/2-先出席这些天,再出席其它天直至此人共计出席了m/2天,且此后处理任何人时只需使其出席其所有闲置可选天
    超过m/2-本组数据无解
最后需要检查一下是否所有天都安排上了.

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;

vector<int> d[100010];  // who appeared on day i
vector<int> p[100010];  // people i appeared on which day
int n, m, ct[100010];   // appear time of people
int ans[100010];

void solve() {
    scanf("%d%d", &n, &m);
    memset(ans, 0, sizeof(ans));
    memset(ct, 0, sizeof(ct));
    for (int i = 1; i <= n; i++) p[i].clear();
    for (int i = 1; i <= m; i++) {
        d[i].clear();
        int k, x;
        scanf("%d", &k);
        while (k--) {
            scanf("%d", &x);
            d[i].push_back(x);
            p[x].push_back(i);
            ct[x]++;
        }
    }

    int bd = (m - 1) / 2 + 1;
    bool good = false;
    for (int i = 1; i <= n; i++) {
        if (good) {
            for (auto j : p[i])
                if (!ans[j]) ans[j] = i;
        } else if (ct[i] <= bd)
            for (auto j : p[i]) ans[j] = i;
        else {
            good = true;
            int c = 0;
            for (int j = 1; j <= m; j++)
                if (!ans[j] && d[j].size() == 1 && d[j][0] == i) {
                    ans[j] = i;
                    c++;
                }
            if (c > bd) {
                puts("NO");
                return;
            }
            for (auto j : p[i]) {
                if (c == bd) break;
                if (!ans[j]) {
                    ans[j] = i;
                    c++;
                }
            }
        }
    }

    for (int i = 1; i <= m; i++)
        if (!ans[i]) {
            puts("NO");
            return;
        }
    puts("YES");
    for (int i = 1; i <= m; i++) printf("%d ", ans[i]);
    puts("");
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) solve();

    return 0;
}

总结就是注意系数,并且敢于假设前提,往后多想几步.
不能一厢情愿地认为是某种算法,这可能是一道构造题.

原文地址:https://www.cnblogs.com/Gaomez/p/14564412.html