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]\)以及邊\((i,j),(j,k)\),則有:

  • \(\dfrac{l_i+r_i}{2} \leq l_j \leq r_j \leq \dfrac{l_k+r_k}{2}\),不難知則此時最優選擇一定是\(a_i=l_i,l_j \leq a_j \leq r_j,a_k=r_k\)
  • \(l_j \leq \dfrac{l_i+r_i}{2} \leq r_j \leq \dfrac{l_k+r_k}{2}\),此時最優選擇是\(a_i=r_i,a_j=l_j,a_k=r_k\)
  • \(\dfrac{l_i+r_i}{2} \leq l_j \leq \dfrac{l_k+r_k}{2} \leq r_j\),此時最優選擇是\(a_i=l_i,a_j=r_j,a_k=l_k\)
  • \(l_j \leq \dfrac{l_i+r_i}{2} \leq \dfrac{l_k+r_k}{2} \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-1} _ {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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM