Codeforces Round #695 (Div. 2)

Codeforces Round #695 (Div. 2)

A. Wizard of Orz

题意: 有n个灯,每个灯一开始都是0,每一秒钟每个灯显示的数字会往上一位,但是由于每个灯只能显示单个数位,所以从9增大时得到的是0.在任意时刻,你可以指定唯一一个灯被停止,下一秒后,他相邻的两个灯也会进入暂停状态.问,在合理的设置一个灯被停止之后,所有灯停止,显示的最大的数是多少

**题解: **

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
int const MAXN = 2e5 + 10;
int n, m, T;

int main() {
    cin >> m;
    while(m--) {
        cin >> T;
        if (T == 1) cout << "9";
        else if (T == 2) cout << "98";
        else if (T == 3) cout << "989";
        else {
            cout << "989";
            n = 0;
            for (int j = 0; j < T - 3; ++j) {
                cout << n;
                n++;
                if (n == 10) n = 0;
            }
        }    
        cout << endl;
    }
    return 0;
}

B. Hills And Valleys

题意: 给定一个长度为n的数组,我们定义山峰为中间点高于两边,定义山谷为中间点低于两边。现在能够改变一个a[i]的权值为任意值,问最少的山峰和山谷数目是多少?

**题解: ** 对于某一个点来说,为了让山峰山谷数目最少,我要不然把它改成前一个点的权值,要不然把它改成后一个点的权值。那么全部都试一下就行了。

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;

int const MAXN = 3e5 + 10;
int n, T;
int a[MAXN];

int solve (int x) {
    if (x == 1 || x == n) return 0;
    if (a[x - 1] > a[x] && a[x] < a[x + 1]) return 1;
    if (a[x - 1] < a[x] && a[x] > a[x + 1]) return 1;
    return 0;
}

int main() { 
    cin >> T;
    while (T--) {
        cin >> n;
        for (int i = 1; i <= n; i++) scanf("%d", a + i);
        int res = 0;
        for (int i = 2; i < n; i++) res += solve(i);
        int maxv = 0;
        for (int i = 2; i < n; i++) {
            int t = a[i];
            int X = solve(i - 1) + solve(i) + solve(i + 1);
            a[i] = a[i - 1];
            int Y = solve(i - 1) + solve(i) + solve(i + 1);
            maxv = max(maxv, X - Y);
            a[i] = a[i + 1];
            Y = solve(i - 1) + solve(i) + solve(i + 1);
            maxv = max(maxv, X - Y);
            a[i] = t;
        }
        printf("%d\n", res - maxv);
    }
    return 0;
}

C. Three Bags

题意: 有三个袋子,每个袋子中有若干个数。我们可以进行如下操作:从袋子 x 中取出数 a , 从另一个袋子 y 中取出数 b ,然后将 a − b放回袋子 x 中。重复操作直到只剩一个数,问我们最后能得到的数最大是多少。

**题解: ** 看到题目就感觉是在数字前面填写+、-号的问题,然后分析了半天也想不出 。题解参考这个大佬:https://blog.csdn.net/qq_44951010/article/details/112392510?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control

代码:

#include <bits/stdc++.h>

typedef long long LL;
using namespace std;
int const MAXN = 300005;

int a[MAXN], b[MAXN], c[MAXN];

int main(void) {
    int n1, n2, n3;
    scanf("%d%d%d", &n1, &n2, &n3);
    LL sum = 0, s1 = 0, s2 = 0, s3 = 0;
    for (int i = 0; i < n1 + n2 + n3; ++i) {
        int t;
        scanf("%d", &t);
        if (i < n1)
            a[i] = t, s1 += t;
        else if (i < n1 + n2)
            b[i - n1] = t, s2 += t;
        else
            c[i - n1 - n2] = t, s3 += t;
    }
    sum = s1 + s2 + s3;
    sort(a, a + n1);
    sort(b, b + n2);
    sort(c, c + n3);
    long long sub = min(s1, min(s2, s3));
    sub = min(sub, (LL)a[0] + b[0]);
    sub = min(sub, (LL)a[0] + c[0]);
    sub = min(sub, (LL)b[0] + c[0]);
    printf("%lld\n", sum - 2 * sub);
    return 0;
}

D. Sum of Paths

题意: 一个机器人一开始可以从1 ~ n的任意一个位置开始移动,允许移动k步,每次移动k步后形成一条路径,这次移动的贡献值为这条路径上每个点的权值和。机器人所有可以走的路径和为机器人的权值和。现在给定m次操作,每次操作能够使得a[x] = y,问每次机器人的权值和为多少?

**题解: ** 由于本题有m次操作,如果我们能够求出一开始的权值和,那么每次操作只需要修改一个点,减去一个原来的值,加上一个新的值即可。对于权值和,等于每个点的次数 * 每个点的权值。我们可以使用dp来维护每个点出现的次数。

f [ i ] [ j ] f[i][j] f[i][j]:走i步到达j点的方案数。那么有转移方程: f [ i ] [ j ] = ( f [ i − 1 ] [ j + 1 ] + f [ i − 1 ] [ j − 1 ] ) f[i][j] = (f[i - 1][j + 1] + f[i - 1][j - 1]) % mod; f[i][j]=(f[i1][j+1]+f[i1][j1])

然后维护m次操作即可。

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
int const MAXN = 5e3 + 10, mod = 1e9 + 7;
int n, m, T, k;
LL f[MAXN][MAXN], a[MAXN], cnt[MAXN];  // f[i][j]:走i步到达j点的方案数

int main() {
    cin >> n >> k >> m;
    for (int i = 1; i <= n; ++i) scanf("%lld" , a + i), f[0][i] = 1;
    for (int i = 1; i <= k; ++i) 
        for (int j = 1; j <= n; ++j) {
            if (j == 1) f[i][j] = f[i - 1][j + 1]; 
            else if (j == n) f[i][j] = f[i - 1][j - 1];
            else f[i][j] = (f[i - 1][j + 1] + f[i - 1][j - 1]) % mod;
        }
    
    LL sum = 0;
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j <= k; ++j) {
            cnt[i] = (f[j][i] * f[k - j][i] % mod + cnt[i]) % mod;
        }
        sum = (sum + cnt[i] * a[i]) % mod;
    }

    for (int i = 1; i <= m; ++i) {
        LL x, y;
        scanf("%lld%lld", &x, &y);
        sum = (((sum - cnt[x] * a[x]) % mod) + mod) % mod;
        a[x] = y;
        sum = (sum + cnt[x] * a[x] % mod) % mod;
        printf("%lld\n", sum);
    }
    return 0;
}

E. Distinctive Roots in a Tree

题意: 给你一个n个点的无根树,每个点有点权,问你有多少个点满足以它为根的树中每一条根到叶子的路径上都没有权值相同的点。

**题解: ** 题解直接看这个大佬的:https://blog.csdn.net/forever_shi/article/details/112467674

感觉这个代码好妙呀,直接把2种情况拢在一起写了,然后一种情况分成2半写,要对dfs有很深的理解才能写得出来。想到了思路不知道怎么实现。

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
int const MAXN = 2e5 + 10, MAXM = MAXN * 2;
int n, m, T;
int e[MAXM], ne[MAXM], idx, h[MAXN], a[MAXN], tot, dfn[MAXN], cnt, ed[MAXN];
LL sum[MAXN];
unordered_map<int, int> st, num, cur_num;

void add(int a, int b ){
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void dfs(int u, int fa) {
    dfn[u] = ++cnt;
    int pre1 = cur_num[a[u]];
    cur_num[a[u]]++;
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == fa) continue;
        int pre2 = cur_num[a[u]];
        dfs(j, u);
        if (cur_num[a[u]] > pre2) {
            sum[1]++;
            sum[dfn[j]]--;
            sum[ed[j] + 1]++;
        }
    }
    ed[u] = cnt;
    if (cur_num[a[u]] - pre1 < num[a[u]]) {
        sum[dfn[u]]++;
        sum[ed[u] + 1]--;
    }
    return;
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; ++i) scanf("%d", a + i), h[i] = -1, num[a[i]]++;
    for (int i = 1, a, b; i <= n - 1; ++i) {
        scanf("%d%d", &a, &b);
        add(a, b), add(b, a);
    }
    dfs(1, -1);
    int res = 0;
    for (int i = 1; i <= n; ++i) {
        sum[i] += sum[i - 1];
        if (!sum[i]) res++;
    }
    printf("%d\n", res);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值