Codeforces Round #766 (Div. 2)

A - Not Shading

题目大意

每次选择一个黑格子, 可以将同行或者同列的格子全部变为黑色, 问最少需要多少次可以将目标格子涂成黑色

思路

  • 不存在黑格子, 那么就是不能涂成黑色, -1
  • 本来就是黑格子, 0次
  • 同行或者同列, 1次
  • 不同行且不同列, 那么最多只需要两次啦, 这个常识

代码

#include <iostream>
 
using namespace std;
 
const int N = 60;
 
int n, m, x, y;
char g[N][N];
 
int main() {
    int T;
    cin >> T;
    while (T --) {
        cin >> n >> m >> x >> y;
        for (int i = 1; i <= n; i ++) cin >> (g[i] + 1);
 
        bool f = false;
        for (int i = 1; i <= n; i ++) {
            for (int j = 1; j <= m; j ++ ) {
                if (g[i][j] == 'B') {
                    f = true;
                    break;
                }
            }
        }
        if (!f) {
            puts("-1");
            continue;
        }
 
        if (g[x][y] == 'B') puts("0");
        else {
            for (int i = 1; i <= max(n, m); i ++) {
                if (i <= n && g[i][y] == 'B') {
                    puts("1");
                    f = false;
                    break;
                }
                if (i <= m && g[x][i] == 'B') {
                    puts("1");
                    f = false;
                    break;
                }
            }
            if (f) puts("2");
        }
    }
    return 0;
}

B - Not Sitting

题目大意

凳子分为两种, 没有颜色和粉色, 有两个人(以下简称A和B), 开始时A可以将k个凳子涂成粉色, B不坐粉色的凳子, A想要尽可能远离B, 而B想要尽可能靠近A, 问在k分别取0到n* m - 1的情况下, A和B之间的最大距离是多少

思路

B每次都会选尽可能靠近中间的座位,而A每次会选四个角落中的某个。但是我们要是这样单纯的想其实很难模拟出来涂色的规律。我们不妨倒着想,对于每一个被涂色的座位,总是被涂色前B的最佳选择,因为他们的选择都是明智的,也就是说我们可以将被涂色的座位作为B的位置。因为每个位置都会被涂色,所以每个位置都会作为B的座位。这样问题就简化为每个位置距离四个角落的最大值,我们将他们放到一个数组中,然后排序输出即可,因为k越大时越有利于A同学,两者的距离也就越大。

代码

#include <iostream>
#include <vector>
#include <algorithm>
 
using namespace std;
 
const int N = 60;
 
int n, m;
vector<int> a;
 
int main() {
    int T;
    cin >> T;
    while (T --) {
        a.clear();
        cin >> n >> m;
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= m; j ++ )
                a.push_back(max(i - 1, n - i) + max(j - 1, m - j));
        sort(a.begin(), a.end());
        for (int i = 0; i < n * m; i ++ ) cout << a[i] << ' '; cout << endl;
    }
    return 0;
}

C - Not Assigning

题目大意

要构建一棵树, 树的边已经给出, 要求树的每条边的权值是素数, 相邻两条边的和也是素数, 要求按照顺序输出权值

思路

素数: 2, 3, 5, 7, 11, 13, 17, 19, 21...
除2以外所有的素数都是奇数, 也就是说在树上时不能相邻, 这样的话, 树上就不能有一个节点的度大于3, 树必须退化为一条链
而这样被2分割开之后发现, 不管选哪一个素数都一样, 所以我们只要让2和任意一个素数轮流出现就可以了

代码

#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
#include <map>
 
using namespace std;
typedef pair<int, int> PII;
 
const int N = 1e5 + 10;
 
int n;
int d[N];
bool st[N];
int h[N], e[N * 2], ne[N * 2], idx;
PII edge[N];
map<PII, int> mp;
 
void add(int a, int b) {
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx ++;
}
 
void dfs(int u, int fa, int c) {
    for (int i =  h[u]; i != -1; i = ne[i]) {
        int j = e[i];
        if (j == fa || st[j]) continue;
        st[j] = true;
        mp[{u, j}] = mp[{j, u}] = c;
        dfs(j, u, 7 - c);
    }
}
 
int main() {
    int T;
    cin >> T;
    while (T --) {
        idx = 0;
        memset(st, false, sizeof st);
        memset(h, -1, sizeof h);
        memset(d, 0, sizeof d);
 
        cin >> n;
        bool f = false;
        for (int i = 1; i < n; i ++) {
            int a, b;
            cin >> a >> b;
            if (a > b) swap(a, b);
            d[a] ++;
            if (d[a] > 2) f = true;
            d[b] ++;
            if (d[b] > 2) f = true;
            add(a, b);
            add(b, a);
            edge[i] = { a, b };
        }
 
        if (f) {
            puts("-1");
        }
        else {
            for (int i = 1 ; i <= n; i ++)
                if (d[i] == 1)
                    dfs(i, -1, 2);
            for (int i = 1; i < n; i ++) {
                cout << mp[edge[i]] << ' ';
            }
            cout << endl;
        }
    }
    return 0;
}

D. Not Adding

题目大意

每次选取两个数, 如果他们的gcd不在数组中, 就将他们的gcd添加到数组中去, 问能这样操作多少次

思路

这题我们最终是看有多少个gcd可以被加在数组后面,因为gcd只会变小不会变大,所以我们最多将1~A中的所有数都加进来(A是整个数组元素的最大值)。所以我们可以考虑对于暴力1到A的每一个数,看这个数(下面记为x)能否由数组中的gcd凑出来。一共只有两种情况:

  • x本来就是数组元素的一个数
  • x是多个数组元素的gcd
    对于以上两种情况,我们可以用筛法的思想,对于每个x我们枚举x的倍数y,如果y在数组元素中,我们对其做一次gcd,如果最后的所有的y的gcd就是x,那么说明x一定是可以被凑出来的,且这样不会漏掉某些答案。(因为x的倍数一定是含有因子x的,所以他们的gcd最多只会比x大,比如x,2x,3x等。若x本身就是数组元素,那么gcd直接就是x,;若x是由某些子元素gcd合成的那么一定是最小的gcd,而更大的x会在后面遍历到)

代码

#include <iostream>
 
using namespace std;
 
const int N = 1e6 + 10;
 
int n;
bool st[N];
 
int gcd(int a, int b) {
    return b ? gcd(b, a % b) : a;
}
 
int main() {
    cin >> n;
    int maxv = 0;
    for (int i = 1; i <= n; i ++) {
        int x;
        cin >> x;
        st[x] = true;
        maxv = max(maxv, x);
    }
 
    int cnt = 0;
    for (int i = 1; i <= maxv; i ++ ) {
        int div = 0;
        for (int j = 1; j * i <= maxv; j ++ )
            if (st[j * i]) div = gcd(div, i * j);
 
        if (i == div) cnt ++;
    }
    cout << cnt - n << endl;
    return 0;
}

E - Not Escaping

题目大意

从(1, 1) 一直到(n, m), 每次可以在每个楼层横向移动, 权值为x[i] * abs(i - j), 也可以借助梯子爬行, 每次权值为\(h_i\)

思路

直接存一个二维矩阵是肯定不行的, 于是选择离散化所有点, 毕竟梯子只有1e5, 点数最多就是2e5 + 2(起点和终点)

  • 对于每一层来说, 可以从左边或者右边横向移动
  • 纵向移动就通过梯子
    \(f_i\)表示到离散化后点i的最小花费
  • 从每一层开始枚举, 同一层内可以从任意点到达, 但是从A到B必须经过A到B中间的每一个点, 这里是一个小的dp, 从该层的最左边和最右边分别走, 可以保证每一层的移动最小
  • 然后枚举该层的所有梯子, 注意点的离散化

代码

#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>
 
#define x first
#define y second
 
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
 
const int N = 2e5 + 10;
 
int n, m, k, cnt;
int x[N];
LL f[N];
vector<PII> row[N];
PII edge[N];
 
int main() {
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int T;
    cin >> T;
    while (T --) {
        cnt = 0;
 
        cin >> n >> m >> k;
        for (int i = 1; i <= n; i ++ ) cin >> x[i], row[i].clear();
        for (int i = 1; i <= 2 * k + 3; i ++ ) f[i] = 1e18, edge[i] = {0, 0};
        row[1].push_back({ 1, ++ cnt });
        for (int i = 1; i <= k; i ++ ) {
            int a, b, c, d, h;
            cin >> a >> b >> c >> d >> h;
            row[a].push_back({ b, ++ cnt });
            row[c].push_back({d, ++ cnt});
            edge[cnt - 1] = { cnt, -h };
        }
        row[n].push_back({ m, ++ cnt });
 
        f[1] = 0;
        for (int i = 1; i <= n; i ++ ) {
            sort(row[i].begin(), row[i].end());
 
            for (int j = 1; j < row[i].size(); j++)
                f[row[i][j].y] = min(f[row[i][j].y], f[row[i][j - 1].y] + 1LL * x[i] * (row[i][j].x - row[i][j - 1].x));
 
            for (int j = row[i].size() - 2; j >= 0; j--)
                f[row[i][j].y] = min(f[row[i][j].y], f[row[i][j + 1].y] + 1LL * x[i] * (row[i][j + 1].x - row[i][j].x));
 
            for (auto j : row[i])
                if (edge[j.y].x && f[j.y] != 1e18)
                    f[edge[j.y].x] = min(f[edge[j.y].x], f[j.y] + edge[j.y].y);
        }
 
        //for (int i = 1; i <= cnt; i ++ ) cout << f[i] << ' '; cout << endl;
        if (f[cnt] == 1e18) cout << "NO ESCAPE" << endl;
        else cout << f[cnt] << endl;
    }
    return 0;
}
posted @ 2022-01-21 20:58  哇唔?  阅读(52)  评论(0编辑  收藏  举报