1. 程式人生 > 其它 >CF Round 765 Div2 題解

CF Round 765 Div2 題解

A題 Ancient Civilization

\(T(1\leq T \leq 100)\) 組資料。

定義 \(\operatorname{d}(x,y)\) 為數 \(x,y\) 間的距離,值為二進位制下不同的位置的數量之和。(例如 \((10010)_2\)\((01011)_2\),這兩個數的距離為 3)。

現在,我們有 \(n\) 個數,第 \(i\) 個數記為 \(x_i\)。現在嘗試求出一個數 \(y\),使得 \(\sum\limits_{i=1}^n\operatorname{d}(x_i,y)\) 最小,並輸出 \(y\)

\(n\leq 100,0\leq x_i,y < 2^l\)

,其中 \(l\) 為題目中給定的一個常數,且 \(1\leq l \leq 30\)

計算每一位的貢獻,顯然為了距離最小,優先讓 \(y\) 的這一位和大多數保持一致(例如有 a 個數的第 xx 位為 1, b 個數的第 xx 位為 0,那麼 \(y\) 的這一位應當根據 a 和 b 的大小關係來決定,哪個大選哪個)。對每一位均進行該操作,總複雜度為 \(O(Tln)\)

#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int n, l, x[N];
int solve() {
    cin >> n >> l;
    for (int i = 1; i <= n; ++i)
        cin >> x[i];

    int res = 0;
    for (int i = 0; i < 30; ++i) {
        int a = 0, b = 0;
        for (int k = 1; k <= n; ++k)
            if ((x[k] >> i) & 1) a++;
            else b++;
        if (a > b) res += 1 << i;
    }
    return res;
}

int main()
{
    int T;
    cin >> T;
    while (T--) cout << solve() << endl;
    return 0;
}

B題 Elementary Particles

\(T(1\leq T \leq 100)\) 組資料。

給定一個長度為 \(n\) 的數列 \(\{a_n\}\)。如果存在兩個子區間 \([l_1,r_1],[l_2,r_2]\),他們互不相同(即不可能 \(l_1=l_2\)\(r_1=r_2\))長度相同且存在某一位相同(例如 \([4,3,2,1]\)\([5,3,4,8]\),這兩個區間就是長度相等,且第二位相同),那麼我們稱這兩個區間滿足性質A

問,我們在這個數列中能找到的最長的兩個滿足性質A的子區間的長度為多少?(無解時輸出 -1

\(2\leq n \leq 150000,1\leq a_i\leq 150000,\sum n\leq 3*10^5\)

似乎只要某一位相同就能湊出一對區間,所以直接掃一遍,無相同元素則判無解。

如果有相同元素,那麼即可以此進行拓展,來看看最多可以延申多長。我們假設位置 \(i,j\)\(i<j\))的元素相同,那麼區間長度最長為 \((n-j)+(i-1)+1=n-(j-i)\)。(也就是說,這個元素作為整個區間的第 \(i\) 個元素,此時區間長度最長,推導過程就是依次讓 \(a_j\) 為區間的第 \(1,2,\cdots,i\) 項)

有了數學基礎,那麼問題就轉變為了:找出兩個相同元素,且他們在數列上的距離最短。受多組資料和智育所限,我們不妨開一個 \(map\) 來維護每個元素的上一個位置,每當掃到一個新元素的時候都更新一下,並重新計算最短距離。總複雜度為 \(O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;
const int N = 300010;
int n, a[N];
map<int, int> vis;
int solve() {
    vis.clear();

    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    int dis = n + 1;
    for (int i = 1; i <= n; ++i) {
        if (vis[a[i]]) dis = min(dis, i - vis[a[i]]);
        vis[a[i]] = i;
    }
    return n - dis;
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--) printf("%d\n", solve());
    return 0;
}

C題 Road Optimization

題意略。

一個 DP,第一維記錄當前要到第 \(i\) 個杆,第二位記錄當前已經保留了 \(j\) 個杆(而不是拔掉了幾個)。

這樣寫之後,狀態轉移方程就相對好寫一些了:

\[dp(i,x)=\max_{1\leq j < i} dp(j,x-1)+a_j(d_i-d_j) \]

(注:要到第 \(i\) 個杆,說明還沒有到,所以這個杆子暫時不考慮拔不拔,留在以後轉移再處理

別的怎麼統計答案,輸出啥的就不細說了,看程式碼:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 510;
int n, k;
LL len, d[N], a[N];
LL dp[N][N];
int main()
{
    //read
    cin >> n >> len >> k;
    for (int i = 1; i <= n; i++)
        cin >> d[i];
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    //solve
    d[n + 1] = len;
    memset(dp, 0x3f, sizeof(dp));
    dp[1][0] = 0;

    for (int i = 1; i <= n + 1; i++)
        for (int j = 1; j < i; j++)
            for (int x = 1; x <= j; x++)
                dp[i][x] = min(dp[i][x], dp[j][x - 1] + a[j] * (d[i] - d[j]));

    LL ans = 1e18;
    for (int i = 0; i <= k; i++)
        ans = min(ans, dp[n + 1][n - i]);
    cout << ans;
    return 0;
}