Codeforces Round #695 (Div. 2)

A. Wizard of Orz

大意:

有n个时钟,都从0开始计时,每秒都会集体+1,如果当前是9,那么下一秒会变成0

现在可以在任意时间选择一个位置,使这个位置上的时钟停下来,然后过一秒钟后,相邻两个时钟会停下来,再过一秒钟后,再向外的两个时钟会停下来.....

最后全部的时钟停下来后,所有的数字可以组成一个长度为n的数,问这个数最大是多少

思路:

为了使数字最大,那么肯定是希望第一个位置在9停下,然后第二个位置相应的在8停下,如果一开始选择的是第二个位置,那么可以使第三位为9,此时数字最大

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int t, n;
int main() {
    cin >> t;
    while (t--) {
        cin >> n;
        if (n == 1)
            cout << 9;
        else if (n == 2)
            cout << 98;
        else if (n == 3)
            cout << 989;
        else {
            cout << 989;
            for (int i = 0; i < n - 3; i++) {
                cout << i % 10;
            }
        }
        cout << endl;
    }
    return 0;
}

B. Hills And Valleys

大意:

给出n个数,现在定义若(a[i-1]>a[i])(a[i]<a[i+1])或者(a[i-1]<a[i])(a[i]>a[i+1]),则数列的威胁值+1

现在可以任选一个数,将其变为任意一个数,从而得到数列的一个新的威胁值,问这个威胁值最少可以是多少

思路:

因为必须是严格上升或者下降才能+1,所以如果将(a[i])改为(a[i-1])或者(a[i+1]),就会使得分数变化,而修改一个位置上的数,最多会影响三个位置的取值,所以只需要枚举一下修改的位置即可

#include <bits/stdc++.h>

using namespace std;

const int N = 3e5 + 5;
typedef long long LL;
int t, n;
int a[N];

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

int main() {
    cin >> t;
    while (t--) {
        cin >> n;
        for (int i = 0; i < n; i++) {
            cin >> a[i];
        }
        int sum = 0;
        int mx = 0;
        for (int i = 0; i < n; i++) {
            sum += judge(i);
            if (i != 0 && i != n - 1) {
                int tmp = 0;
                int ori = 0;
                tmp = judge(i - 1) + judge(i) + judge(i + 1);
                int pre = a[i];
                ori = tmp;
                a[i] = a[i - 1];
                tmp = min(tmp,judge(i - 1) + judge(i) + judge(i + 1));
                a[i] = a[i + 1];
                tmp = min(tmp,judge(i - 1) + judge(i) + judge(i + 1));
                mx = max(mx, ori - tmp);
                a[i] = pre;
            }
        }
        cout << sum - mx << endl;
    }
    return 0;
}

C. Three Bags

大意:

给出三个背包,每个背包里有几个数字

每次操作可以从两个不同的背包里面各取出一个数字a和b,然后令a=a-b,然后把a放回去,b丢弃

问最后只剩下一个数时,这个数最大为多少

思路:

假设背包的编号为abc

两种情况:

一种是先将b1到bn全挂到a1上,然后把c2到cn全挂到a1上,最后把a1到an全挂到c1上,这样就是a数组全部取负,b和c数组全部为正

一种是留出b和c中最小的那个数取负,其他都取正即可

#include <bits/stdc++.h>

using namespace std;

const int N = 3e5 + 5;
typedef long long LL;
int n[3];
LL a[3][N], minx[3], sum[3];
int main() {
    for (int i = 0; i < 3; i++) cin >> n[i];
    for (int i = 0; i < 3; i++) {
        minx[i] = 0x3f3f3f3f;
        sum[i] = 0;
        for (int j = 0; j < n[i]; j++) {
            cin >> a[i][j];
            minx[i] = min(minx[i], a[i][j]);
            sum[i] += a[i][j];
        }
    }
    LL res = -0x3f3f3f3f;
    for (int i = 0; i < 3; i++) {
        LL tmp = 0;
        for (int j = 0; j < 3; j++) {
            if (i == j)
                tmp -= sum[j];
            else
                tmp += sum[j];
        }
        res = max(res, tmp);
        //cout << tmp << endl;
    }
    for (int i = 0; i < 3; i++) {
        LL tmp = 0;
        for (int j = 0; j < 3; j++) {
            if (i == j)
                tmp += sum[j];
            else
                tmp += sum[j]-2*minx[j];
        }
        res = max(res, tmp);
        //cout << tmp << endl;
    }
    cout << res << endl;
    return 0;
}

D. Sum of Paths

大意:

一个长度为n的数轴,一个机器人在从任意一个位置开始,走k步,每步都可以向左或向右走1步。

走k步之后会得到一条轨迹,轨迹的权值就是每个点的权值和,那么对于这个长度为n的数轴,就可以求出所有轨迹的权值和。

现在给出m次修改,将x位置上的权值改为y,输出每一次修改后的所有轨迹的权值和

思路:

首先(dp[i][j])代表在第j步走到第i位置的方案数,转移方程为:

(dp[i][j]=dp[i+1][j-1]+dp[i-1][j-1])

然后计算(c[i])代表第i个位置上的数贡献了多少次:

$c[j] += dp[j][i] * dp[j][k - i] $

此时(dp[j][k - i])代表从j这里走出去k-i次的方案数,因为和别的地方走k-i次到j处的方案数相同,所以可以直接用

最后就是每次计算权值和即可

#include <bits/stdc++.h>

using namespace std;

const int N = 5e3 + 5;
typedef long long LL;
int n, k, m;
const int mod = 1e9 + 7;
LL a[N], dp[N][N], c[N], sum = 0;
int main() {
    cin >> n >> k >> m;
    for (int i = 1; i <= n; i++) dp[i][0] = 1;
    for (int i = 1; i <= k; i++) {
        for (int j = 1; j <= n; j++) {
            if (j == 1)
                dp[j][i] = dp[j + 1][i - 1];
            else if (j == n)
                dp[j][i] = dp[j - 1][i - 1];
            else
                dp[j][i] = (dp[j - 1][i - 1] + dp[j + 1][i - 1])%mod;
        }
    }
    for (int i = 0; i <= k; i++) {
        for (int j = 1; j <= n; j++) {
            c[j] += dp[j][i] * dp[j][k - i] % mod;
            c[j] %= mod;
        }
    }
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        sum += a[i] * c[i] % mod;
        sum %= mod;
    }
    while(m--){
        int x,y;
        cin>>x>>y;
        sum -= a[x] * c[x] % mod;
        sum = (sum + mod) % mod;
        a[x] = y;
        sum += a[x] * c[x] % mod;
        sum = (sum + mod) % mod;
        cout << sum << endl;
    } return 0;
}

E. Distinctive Roots in a Tree

大意:

定义一个点为distinctive roots,如果从从这个点到树上其他点的路径中,所有的权值只出现过一次。

求出distinctive roots的个数

思路:

直接求哪个点是distinctive roots没有思路,可以考虑删去全部不符合条件的点,剩下的都是distinctive roots

先随便找一个点开始dfs,然后对于每个点,如果子树中存在和这个点的权值相同的点,那么这个点的其他子树都不能符合条件,而且这个点之上的所有点都不符合条件。

如果这个和这个点权值相同的点不全在子树上,那么这个点的所有子树都不符合条件

这两种情况通过树上差分标记一下即可,需要注意的是第一种情况难以直接全标记出来,可以结合第二种情况进行标记

#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 5;
typedef long long LL;
int n, w[N], c[N], xu[N], cnt = 0,num,have[N],ed[N],total[N];
vector<int> mp[N];
map<int, int> Hash;
void dfs(int now, int fa) {
    int hashv = Hash[w[now]];
    xu[now] = ++cnt;
    int prehave = have[hashv];
    have[hashv]++;
    for (int i = 0; i < mp[now].size(); i++) {
        int ne = mp[now][i];
        if (ne == fa) continue;
        int pre = have[hashv];
        dfs(ne, now);
        if(pre<have[hashv]){
            c[1]++;
            c[xu[ne]]--;
            c[ed[ne] + 1]++;
        }
    }
    ed[now] = cnt;
    if(have[hashv]-prehave<total[hashv]){
        c[xu[now]]++;
        c[ed[now] + 1]--;
    }
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> w[i];
        if (Hash[w[i]] == 0) Hash[w[i]] = ++num;
        total[Hash[w[i]]]++;
    }
    for (int i = 1; i < n; i++) {
        int x, y;
        cin >> x >> y;
        mp[x].push_back(y);
        mp[y].push_back(x);
    }
    dfs(1, 0);
    int res = 0;
    for (int i = 1; i <= n;i++){
        c[i] += c[i - 1];
        if (c[i] == 0) res++;
    }
    cout << res << endl;
    return 0;
}
原文地址:https://www.cnblogs.com/dyhaohaoxuexi/p/14287979.html