Codeforces Round #722 (Div. 2) 题解

Posted 绾纾

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #722 (Div. 2) 题解相关的知识,希望对你有一定的参考价值。

题目链接
这次题目其实蛮坑的,有点恶心人。

A. Eshag Loves Big Arrays

题意

给定一个长度为\\(n\\)的序列,可以选择一段区间将其中严格大于区间平均数的数删掉,可以执行任意多次,问最后得到的序列是什么样的。

思路

由题意不难得出我们删到最后一定只剩下数组中的最小值,因为最小值是永远小于等于区间平均值,所以删到最后就只剩下了最小值。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>

using namespace std;

const int N = 110;

int n;
int g[N];
bool st[N];

int main()
{
    int T;
    cin >> T;
    while (T -- ) 
    {
        cin >> n;
        for (int i = 1; i <= n; i ++ ) cin >> g[i], st[i] = false;

        int res = 0;
        int minv = 10000;
        for (int i = 1; i <= n; i ++ ) minv = min(minv, g[i]);
        for (int i = 1; i <= n; i ++ )  
            if (g[i] == minv) res ++ ;
    
        cout << n -res << endl;
    }

    return 0;
}

B. Eshag Loves Big Arrays

题意

给定一个长度为\\(n\\)的序列,要求选择出其最长的满足\\(|a_i-a_j|\\geq MAX,MAX为子序列最大值\\)

思路

我们考虑一下情况:

  • \\(a\\geq b\\geq 0\\)时,原式等价于\\(a-b \\geq a \\Longleftrightarrow b \\leq 0\\),不成立;
  • \\(a\\geq 0 \\geq b\\)时,原式等价于\\(a-b \\geq a \\Longleftrightarrow b \\leq 0\\),成立;
  • \\(0 \\geq a \\geq b\\)时,原价等价于\\(a-b \\geq a \\Longleftrightarrow b\\leq 0\\),成立。

所以对于一个子序列,一定包含原序列的所有非正数,以及最多一个正数,若有正数,正数选择最小的一定是最优的。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>

using namespace std;

const int N = 100010;

int n;
int g[N];
int s[N];

int main()
{
    int T;
    cin >> T;
    while (T -- ) 
    {
        cin >> n;
        for (int i = 1; i <= n; i ++ ) cin >> g[i];
        
        int res = 0;
        sort(g + 1, g + 1 + n);
        
        int t = 0;
        for (int i = 1; i <= n; i ++ ) 
            if (g[i] <= 0) res ++ ;

        int minv = 0x3f3f3f3f;
        for (int i = 2; i <= n; i ++ ) 
            if (g[i] <= 0) 
            {
                minv = min(minv, g[i] - g[i - 1]);
            }
        
        for (int i = 1; i <= n; i ++ ) 
            if (g[i] > 0) // 特判正数是否可以选择
            {
                if (minv >= g[i]) res ++ ;
                break;
            }
        
        cout << res << endl;
    }

    return 0;
}

C. Parsa\'s Humongous Tree

题意

给定一棵树,树上每个节点\\(i\\)都包含一个值域\\([l_i,r_i]\\),要求每个节点在其值域中选择一个数\\(a_i\\),使得所有的边\\([u,v]\\)\\(\\sum^{n} _ {i=1}|a_u-a_v|\\)最大。

思路

易只当只有两个点时选择边界一定是最有的。
三个点时设点\\(i[l_i,r_i]\\)\\(j[l_j,r_j]\\)\\(k[l_k,r_k]\\),则有:

  • \\(l_i \\leq l_j \\leq r_j \\leq r_k\\),不难知则此时最有选择一定是\\(a_i=l_i,l_j \\leq a_j \\leq r_j,a_k=r_k\\)
  • \\(l_j \\leq l_i \\leq r_j \\leq r_k\\),此时最优选择是\\(a_i=r_i,a_j=l_j,a_k=r_k\\)
  • \\(l_i \\leq l_j \\leq r_k \\leq r_j\\),此时最优选择是\\(a_i=l_i,a_j=r_j,a_k=l_k\\)
  • \\(l_j \\leq l_i \\leq r_k \\leq r_j\\),此时最优选择是\\(a_i=r_i,a_j=l_j,a_k=r_k\\)或者\\(a_i=l_i,a_j=r_j,a_k=l_k\\)

(证明并不难,在此不多赘述)由此不难看出选择边界一定是最优的情况,并且选择边界我们就可有两个点转移到三个点,再转移到更多点,即树形\\(dp\\)

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>

using namespace std;

typedef long long LL;

const int N = 100100, M = 200100;

int n;
int h[N], e[M], ne[M], idx;
int l[N], r[N];
LL f[N][2];

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

void dfs(int u, int fa) 
{
    for (int i = h[u]; ~i; i = ne[i]) 
    {
        int j = e[i];
        if (j == fa) continue;
        dfs(j, u);
        f[u][0] += max(f[j][0] + abs(l[u] - l[j]), f[j][1] + abs(l[u] - r[j]));
        f[u][1] += max(f[j][0] + abs(r[u] - l[j]), f[j][1] + abs(r[u] - r[j]));
    }
}

int main()
{
    int T;
    cin >> T;
    while (T -- ) 
    {
        idx = 0;
        scanf("%d", &n);
        for (int i = 1; i <= n; i ++ ) scanf("%d%d", &l[i], &r[i]), h[i] = -1, f[i][1] = f[i][0] = 0;

        for (int i = 1; i < n; i ++ ) 
        {
            int a, b;
            scanf("%d%d", &a, &b);
            add(a, b), add(b, a);
        }

        dfs(1, -1);

        cout << max(f[1][1], f[1][0]) << endl;
    }

    return 0;
}

D. Kavi on Pairing Duty

题意

\\([0,2n]\\)的数轴上,要求选择任意两个点对点对\\(A,B\\)使得\\(lenth(A)=lenth(B)\\)或者\\(A\\)包含于\\(B\\),求出点对的选择有多少种。

思路

线性\\(dp\\),证明过程比较繁杂,这里只做简单叙述,读者看完状态转移方程就可理解。
状态表示为\\(dp[i]\\)表示从\\([q,2 * i]\\)区间的点对选择。

  • 包含情况,对于区间\\([1,2 * i]\\),当我们将两边点按等长都选择上之后,我们中间的点就可以随便选取,例如选择点对\\((1,2 * i)\\),则中间的区间\\([2,2 * i - 1]\\)可以随便选取即\\(dp[i-1]\\),选择点对\\((1,2 * i - 1),(2, 2 * i)\\),则中间的区间\\([3,2 * i - 2]\\)可以随便选取即\\(dp[i - 2]\\),依次类推;
  • 登场情况,区间长度能被点对长度整除,就能选择,即\\(i\\)的正约数集合。

综上所述,状态转移方程为:\\(dp[i]= \\sum^{i} _ {k=1}dp[k]+v[i]\\)

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>

using namespace std;

typedef long long LL;

const int N = 1000010, mod = 998244353;

int n;
int dp[N];
int v[N];

int main()
{
    cin >> n;

    for (int i = 1; i <= n; i ++ ) 
        for (int j = i; j <= n; j += i) 
            v[j] ++ ;

    LL sum = 1;
    dp[1] = 1;
    for (int i = 2; i <= n; i ++ ) 
    {
        dp[i] = (sum + v[i]) % mod;
        sum = (sum + dp[i]) % mod;
    }

    cout << dp[n] << endl;

    return 0;
}

以上是关于Codeforces Round #722 (Div. 2) 题解的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #722 (Div. 2) 20210525

Codeforces Round #722 (Div. 2)Codeforces-1529 ABC

Codeforces Round #722 (Div. 2)Codeforces-1529 ABCD

Codeforces Round #722 (Div. 2)Codeforces-1529 ABCD

Codeforces Round #722 (Div. 2) 部分题解

Codeforces Round #722 (Div. 2) A~D 题解