1. 程式人生 > 其它 >Educational Codeforces Round 11 覆盤

Educational Codeforces Round 11 覆盤

A

只要往相鄰的裡面插入 1 就好了。

const int MAXN = 1000 + 10;

int n, aa[MAXN];
std::vector<int> ans;

int main() {
    std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    n = read();
    rep (i, 1, n) aa[i] = read();
    rep (i, 1, n - 1) {
        // comp a[i], a[i + 1];
        ans.push_back(aa[i]);
        if (std::__gcd(aa[i], aa[i + 1]) != 1) ans.push_back(1);
    }
    ans.push_back(aa[n]);
    printf("%d\n", (int) ans.size() - n);
    for (auto v : ans) printf("%d ", v);
    puts("");
    return 0;
}

B

暴力根據題意模擬即可。

然而我又雙叒叕讀錯題了,甚至連圖都看錯了,然後重寫了兩遍 QAQ 浪費了不少時間

const int MAXN = 100 + 10;

int n, m;
std::vector<int> window[2], mid[2];

int main() {
    std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    n = read(); m = read();
    rep (i, 1, m) {
        if (i <= 2 * n) {
            window[(i - 1) & 1].push_back(i);
        } else {
            mid[(i - 1) & 1].push_back(i);
        }
    }
    window[0].resize(n + 1); window[1].resize(n + 1);
    mid[0].resize(n + 1); mid[1].resize(n + 1);
    // rep (i, 0, n - 1) {
    //     printf("%d %d %d %d\n", window[0][i], mid[0][i], mid[1][i], window[1][i]);
    // }
    for (int row = 0; row < n; ++row) {
        if (mid[0][row]) printf("%d ", mid[0][row]);
        if (window[0][row]) printf("%d ", window[0][row]);
        if (mid[1][row]) printf("%d ", mid[1][row]);
        if (window[1][row]) printf("%d ", window[1][row]);
    }
    puts("");
    return 0;
}

C. Hard Process

一眼雙指標或者二分答案。和之前做的一個題很相似。

寫雙指標不大熟練所以保底寫了個二分。

const int MAXN = 3e5 + 10;

int n, aa[MAXN];
int k;

bool check(int mid) {
    if (mid == 0) return true;
    int cnt[2] = {0, 0};
    for (int i = 1; i <= mid; ++i) {
        ++cnt[aa[i]];
    }
    for (int l = 1; l <= n; ++l) {
        int r = l + mid - 1;
        if (r > n) break;
        if (cnt[0] <= k) return true;

        // move
        --cnt[aa[l]]; ++cnt[aa[r + 1]];
    }
    return false;
}

int main() {
    std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    n = read(); k = read();
    for (int i = 1; i <= n; ++i) aa[i] = read();
    int l = 0, r = n, ans = 0;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check(mid)) { ans = mid; l = mid + 1; }
        else r = mid - 1;
    }
    printf("%d\n", ans);
    int cnt[2] = {0, 0};
    for (int i = 1; i <= ans; ++i) {
        ++cnt[aa[i]];
    }
    for (int l = 1; l <= n; ++l) {
        int r = l + ans - 1;
        if (r > n) break;
        if (cnt[0] <= k) {
            for (int i = l; i <= r; ++i) aa[i] = 1;
            break;
        }

        // move
        --cnt[aa[l]]; ++cnt[aa[r + 1]];
    }
    rep (i, 1, n) printf("%d ", aa[i]);
    return 0;
}

D. Number of Parallelograms

本來以為是個計算幾何然後打算直接 run 的……

停了幾秒發現好像只是個幌子,因為沒有三點共線,所以直接把所有直線按照斜率分類,統計有多少長度相同的,把答案除以 2 就做完了。

然後寫了一個 long double 存斜率的發現在 map 裡面會出問題……樣例都過不去

接著意識到其實根本就不需要存實數斜率,直接存 {x1 - x0, y1 - y0} 相同的直線個數就可以了(假定 y1 - y0 >= 0)。

然後光榮 WA on 28,很快就意識到沒處理 y1 - y0 = 0x1 - x0 的符號問題,稍微改了一下就過了。

const int MAXN = 2000 + 10;

struct Vector {
    lli x, y;
}; typedef Vector Point;

int n; Point pp[MAXN];

struct E {lli dx, dy;};
bool operator < (const E &x, const E &y) {
    return x.dx == y.dx ? x.dy < y.dy : x.dx < y.dx;
}

std::map<E, lli> mp;

lli sqr(lli x) { return x * x; }

int main() {
    std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n;
    rep (i, 1, n) {
        cin >> pp[i].x >> pp[i].y;
    }
    rep (i, 1, n) {
        rep (j, i + 1, n) {
            lli dx = pp[i].x - pp[j].x;
            lli dy = pp[i].y - pp[j].y;
            if (dy < 0) { dy = -dy; dx = -dx;}
            if (dy == 0) dx = std::abs(dx);
            // printf("dx = %d, dy = %d\n", dx, dy);
            
            ++mp[{dx, dy}];
        }
    }
    // puts("");
    lli ans = 0;
    for (auto v : mp) {
        // printf("dx = %d, dy = %d, sz = %d\n", v.first.dx, v.first.dy, v.second);
        ans += 1ll * v.second * (v.second - 1) / 2ll;
    }
    cout << ans / 2 << endl;
    return 0;
}

E. Different Subsets For All Tuples

計數題哈哈哈哈哈哈哈我不會數數怎麼辦

首先把空序列單獨拿出來,一共 \(m^n\) 個,最後加上。然後計算非空序列。

然後(據說)有一個經典套路是按照子序列的長度分類計算,比如當前我要算的就是長度為 \(i\) 的子序列,有 \(m^i\) 個。記這 \(i\) 個數在原序列中的位置是 \(p_1, p_2 \dots p_i\),值分別為 \(v_1, v_2 \dots v_i\)

然後我們強制讓 \([1, p_1 - 1]\) 之間不出現 \(v_1\)\([p_1 + 1, p_2 - 1]\) 之間不出現 \(v_2\),以此類推,直到 \([p_i + 1, n]\) 最後這一段才可以隨便填。

可以列出一個式子:

\[\sum_{i = 1}^n m^i \sum_{j = i}^n C_{j - 1}^{i - 1} (m - 1)^{j - i} m^{n - j} \]

第一個 \(\sum\) 列舉長度 \(i\),第二個 \(\sum\) 列舉 \(p_i\) 的位置,\(C_{j - 1}^{i - 1}\)\((m - 1)^{j - i}\) 是那些被欽定不能出現某個 \(v_i\) 的位置選數的方案數,\(m^{n - j}\) 是最後那一段的選數方案數。

然後就是大力化簡這個式子:

首先交換求和順序,

\[\sum_{j = 1}^n \sum_{i = 1}^j C_{j - 1}^{i - 1} (m - 1)^{j - i} m^{n - j + i} \]

然後把兩個 \(-1\) 都搞掉並把後面的東西換個形式,

\[\sum_{j = 0}^{n - 1}m^{n - j} \sum_{i = 0}^j C_{j}^{i} (m - 1)^{j - i}m^i \]

然後發現後面這玩意是個二項式定理

\[\sum_{j = 0}^{n - 1}m^{n - j}(2m - 1)^j \]

直接算就好了。

const int HA = 1e9 + 7;

int n, m;

int fp(int a, int b, int p) {
    int r = 1; while (b) { if (b & 1) r = 1ll * r * a % p; a = 1ll * a * a % p; b >>= 1; } return r;
}

int main() {
    std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    n = read(); m = read();
    int ans = fp(m, n, HA);
    for (int j = 0; j <= n - 1; ++j) {
        ans = (ans + 1ll * fp(m, n - j, HA) * fp(2 * m - 1, j, HA) % HA) % HA;
    }
    printf("%d\n", ans);
    return 0;
}

F

好像是個斜率優化 DP……