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

CF Round 775 Div2 題解

A題 Game

給定一個長度為 \(n\) 的一維線性地圖,位置 \(i\) 處可能是陸地或者水。

現在我們要從位置 \(1\) 到達位置 \(n\) 處(保證這兩個地方都是水),我們有兩種方式:

  1. 如果位置 \(i\) 和位置 \(i+1\) 都是陸地,那麼可以不耗費代價來互相移動
  2. 可以從位置 \(x\) 跳到位置 \(y\),消耗代價為 \(|y-x|\) (該操作至多進行一次)

問需要花費至少多少代價來從位置 \(1\) 到達位置 \(n\)

\(1\leq n \leq 100\)

從頭尾分別延伸最長的連續陸地串,然後中間直接跳即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int n, a[N];
int solve() {
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    //
    bool flag = true;
    for (int i = 1; i <= n; ++i)
        if (a[i] == 0) flag = false;
    if (flag) return 0;

    int l, r;
    for (int i = 1; i <= n; ++i)
        if (a[i] == 0) {
            l = i - 1;
            break;
        }
    for (int i = n; i >= 1; i--)
        if (a[i] == 0) {
            r = i + 1;
            break;
        }
    return r - l;
}
int main()
{
    int T;
    cin >> T;
    while (T--) cout << solve() << endl;
    return 0;
}

B題 Game of Ball Passing (數學)

建議直接看原題面

我們對整個陣列從小到大排個序,記整個陣列前 \(n-1\) 個元素之和為 \(L\),最大元素的值為 \(R\)

\(L+1\geq R\) 的時候,不難構造出只含有一個球的方案。

\(L+1<R\) 時,我們發現,每次一個球都會使得 \(R\)\(L\) 的差值減小 1,所以答案為 \(R-L\)

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 100010;
int n;
LL a[N];
int solve() {
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    sort(a + 1, a + n + 1);
    if (a[n] == 0) return 0;
    LL L = 0, R = a[n];
    for (int i = 1; i < n; ++i) L += a[i];

    return max(R - L, 1LL);
}
int main()
{
    int T;
    cin >> T;
    while (T--) cout << solve() << endl;
    return 0;
}

C題 Weird Sum(字首和)

給定一張 \(n\)\(m\) 列的地圖,每個格子都有一個自己的顏色 \(c_{i,j}\)

我們記同一顏色 \(x\) 的格子的集合為 \(S_x\),那麼 \(f(S_x)\) 為集合內格子兩兩曼哈頓距離之和,試求出 \(\sum\limits_{x\in M} f(S_x)\) 的值。(\(M\) 為地圖中所有顏色之和)

\(1\leq n \leq m,nm\leq 10^6,1\leq c_{i,j}\leq 10^6\)

這個顯然,我們開 \(10^6\) 個 vector,講相同顏色的格子放入對應顏色的 vector 裡面,然後直接對每個 vector 求解即可。

不過,我們平方列舉肯定不行,得優化到線性複雜度。

我們考慮座標僅有一維的情況,那麼我們直接將座標排個序,然後做一次字首和,按照算貢獻的方式來統計答案,就能夠將平方列舉優化到線性。

\[\sum\limits_{i=1}^{n-1}\sum\limits_{j=1+1}^n |x_j-x_i|=\sum\limits_{i=1}^{n-1}(\sum\limits_{j=i+1}^n x_j-(n-i)x_i) \]

對於二維的曼哈頓距離,由於兩軸座標互相獨立,互不影響,所以我們直接分別排序統計後相加即可。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int C = 100010;
int n, m;
vector<pair<int, int> > color[C];
LL x[C], y[C], xx[C], yy[C];
LL solve(int c) {
    vector<pair<int, int> > &arr = color[c];
    int cnt = 0;
    for (auto it : arr) x[++cnt] = it.first, y[cnt] = it.second;
    sort(x + 1, x + cnt + 1);
    sort(y + 1, y + cnt + 1);
    for (int i = 1; i <= cnt; ++i)
        xx[i] = xx[i - 1] + x[i], yy[i] = yy[i - 1] + y[i];
    LL res = 0;
    for (int i = 1; i < cnt; ++i) {
        int len = cnt - i;
        res += (xx[cnt] - xx[i]) - x[i] * len;
        res += (yy[cnt] - yy[i]) - y[i] * len;
    }
    return res;
}
int main()
{
    //read
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        for (int j = 1, c; j <= m; ++j) {
            cin >> c;
            color[c].push_back(make_pair(i, j));
        }
    //solve
    LL ans = 0;
    for (int i = 1; i < C; ++i) ans += solve(i);
    cout << ans << endl;
    return 0;
}

D題 Integral Array(倍數列舉)

給定一個數組 \(\{a_n\}\),問是否對於內部任意兩個元素 \(x,y(x\geq y)\)\(\lfloor\frac{x}{y}\rfloor\) 也在陣列內?

\(1\leq n\leq 10^6,1\leq a_i\leq c \leq 10^6\)

一個偏暴力的 TLE 寫法

我們將陣列排個序並 unique 一下,然後從小到大依次將數放進去,每次加入一個新數字的時候,只列舉 \(\sqrt{a_i}\) 範圍內的數,如果這個數存在,就看看整除的結果在不在陣列內;如果不在,則看範圍 \([L,R]\) 之內的數是否存在(\(L,R\) 滿足 \(a_i\) 除以區間內的數得到的結果都等於這個數,可以 \(O(1)\) 的計算 \(L,R\))。考慮到單點修改和區間查詢,所以我們維護一個 set 或者樹狀陣列/線段樹。

總複雜度 \(O(n\sqrt c\log c)\),對於 \(10^5\) 的資料還能掙扎一下,但是 \(10^6\) 屬實有點為難了。

正解:倍數列舉

我們一開始就建立好 vis 陣列並做一次字首和(我們上面那個方法是動態插入的,所以有一個 \(O(\log n)\) 的複雜度,這裡直接是一開始全部處理好,然後實現 \(O(1)\) 查詢)。

我們列舉每一種值,倘若這個值存在,我們記為 \(x\),那麼我們直接列舉 \(\lfloor\frac{y}{x}\rfloor\) 的值,記為 \(k\),那麼有 \(kx\leq y <k(x+1)\)。我們看看區間 \([kx,kx+x-1]\) 內是否存在數,如果存在,那麼我們就要查詢一下 \(k\) 是否存在了。

這個的複雜度就是純 \(O(n+c\sqrt{c})\),其實也就是少了一個 \(O(\log c)\) 的複雜度。

#include<bits/stdc++.h>
using namespace std;
const int N = 1000010;
int n, c, a[N];
//
int vis[N], pre[N];
inline bool query(int L, int R) { return pre[R] > pre[L - 1]; }
//
bool solve()
{
    //read
    scanf("%d%d", &n, &c);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    //init
    memset(vis, 0, sizeof(int) * (c + 1));
    for (int i = 1; i <= n; ++i)
        vis[a[i]] = 1;
    for (int i = 1; i <= c; ++i)
        pre[i] = pre[i - 1] + vis[i];
    //solve
    for (int x = 1; x <= c; ++x) {
        if (!vis[x]) continue;
        for (int k = 1; k * x <= c; ++k)
            if (query(k * x, min(c, k * x + x - 1)) && !vis[k])
                return false;
    }
    return true;
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--) puts(solve() ? "Yes" : "No");
    return 0;
}