Atcoder Beginner Contest 367 C-F

maniubi發表於2024-08-17

Atcoder Beginner Contest 367 C-F

C - Enumerate Sequences

題意

按字典序升序輸出所有滿足下列條件的序列數量。

  1. 長度為 \(N\)

  2. \(i\) 個元素介於 \(1\)\(R_i\) 之間。

  3. 所有元素之和是 \(K\) 的倍數。

思路

搜尋即可。搜尋時記錄當前選了哪些數和元素之和,最後搜完了判斷一下即可。

程式碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int n, k, a[N], R[N];
void dfs(int x, int sum) {
    if (x == n + 1) {
        if (sum % k) return ;
        for (int i = 1; i <= n; i ++) {
            cout << a[i] << " ";
        }
        cout << "\n";
        return ;
    }
    for (int i = 1; i <= R[x]; i ++) {
        a[x] = i;
        dfs(x + 1, sum + i);
    }
}
void solve() {
    cin >> n >> k;
    for (int i = 1; i <= n; i ++) cin >> R[i];
    dfs(1, 0);
    cout << "\n";
}
int main() {
    int T = 1;
    // cin >> T;
    while (T --) solve();
    return 0;
}

D - Pedometer

題意

一個湖周圍有 \(N\) 個休息區。

從休息區 \(i\) 順時針走到休息區 \(i+1\) 需要 \(A_i\) 步,其中休息區 \(N+1\) 指的是休息區 \(1\)

從休息區 \(s\) 順時針走到休息區 \(t(s \ne t)\) 所需的最小步數是 \(M\) 的倍數。

\((s,t)\) 的可能對數。

思路

容易想到把原序列複製一遍破環成鏈。

把原 \(A\) 陣列做一遍字首和,得到 \(S\)

\(s\)\(t\) 的距離為 \(S_t-S_s\),其中 \(s < t < s + n\)

\(t \ge s + n\) 相當於繞了整個湖一圈,不符合題意。

原問題轉化為了有多少對 \((i,j)\) 滿足 \((i<j<i+n)\)\(S_j-S_i \equiv 0\pmod{M}\)

轉化一下即 \(S_j \equiv S_i \pmod{M}\)

使用一個桶維護 \(S_i\)\(M\) 取餘的結果。

倒序列舉 \(i\),把 \(S_{i+n}\) 從桶中刪除,在桶裡查詢有多少個 \(j\) 滿足條件,並把 \(S_i\) 加入桶中。

預處理時把 \(S_{n+1}\)\(S_{2n}\) 都加入桶中。

程式碼

#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
int n, m, a[N], c[N];
ll ans;
void solve() {
    cin >> n >> m;
    for (int i = 1; i <= n; i ++) 
        cin >> a[i + 1], a[i + n + 1] = a[i + 1],
        a[i + 1] %= m, a[i + n + 1] %= m;
    for (int i = 1; i <= 2 * n; i ++) a[i] += a[i - 1], a[i] %= m;
    for (int i = n + 1; i <= 2 * n; i ++) c[a[i]] ++;
    for (int i = n; i >= 1; i --) {
        c[a[i + n]] --;
        ans += c[a[i]];
        c[a[i]] ++;
    }
    cout << ans << "\n";
}
signed main() {
    int T = 1;
    // cin >> T;
    while (T --) solve();
    return 0;
}

E - Permute K times

題意

給你一個長度為 \(N\) 的序列 \(X\),其中每個元素的都在 \(1\)\(N\) 之間。

以及一個長度為 \(N\) 的序列 \(A\)

輸出在 \(A\) 上執行以下操作 \(K\) 次的結果。

\(B\) 替換 \(A\),其中 \(B_i = A_{X_i}\)

思路

因為 \(1 \le K \le 10^{18}\),暴力列舉肯定不行,考慮使用倍增。

\(f_{i,j}\) 表示 \(i\) 變換 \(2^j\) 次後變成的數,\(i\)\(j\) 均為下標。

\[f_{i,j} = f_{f_{i, j-1},j -1} \]

計算答案時對於每個 \(i\) 計算最後變成的下標 \(j\),輸出 \(A_j\)

計算時若 \(K\) 二進位制下第 \(p\) 位為 \(1\),則將 \(i\) 變為 \(f_{i,p}\)

即將 \(i\)\(2^p\) 次轉換,最後合起來即 \(K\) 次轉換。

程式碼

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 5;
int n, k, a[N], f[N][70], b[N];
void solve() {
    cin >> n >> k;
    for (int i = 1; i <= n; i ++) cin >> f[i][0];
    for (int i = 1; i <= n; i ++) cin >> a[i];
    for (int i = 1; i <= 59; i ++) 
        for (int j = 1; j <= n; j ++)
            f[j][i] = f[f[j][i - 1]][i - 1];
    for (int i = 1; i <= n; i ++) {
        int now = i;
        for (int j = 0; j <= 59; j ++) 
            if (k >> j & 1) now = f[now][j];
        cout << a[now] << " ";
    }
}
signed main() {
    int T = 1;
    // cin >> T;
    while (T --) solve();
    return 0;
}

F - Rearrange Query

題意

給你長度為 \(N\) 的正整數序列:\(A\)\(B\)

您需要依次處理 \(Q\) 個查詢。

給定正整數 \(l_i,r_i,L_i,R_i\)。如果可以重新排列子序列 \((A_{l_i},A_{l_i+1},\ldots,A_{r_i})\) 以匹配子序列 \((B_{L_i},B_{L_i+1},\ldots,B_{R_i})\),則列印是,否則列印否。

思路

\((A_{l_i},A_{l_i+1},\ldots,A_{r_i})\) 中每個數出現的次數和 \((B_{L_i},B_{L_i+1},\ldots,B_{R_i})\) 中每個數出現的次數一樣,則一定可以完成匹配,證明顯然。

如何比較數的出現次數呢?我們想到了雜湊,把每個數的出現次數雜湊起來。

相當於把數的出現次數轉化成一個 \(N\)\(M\) 進位制的數,每位存放 \(i\) 出現的次數,使用自然溢位即可。統計時運用字首和快速計算雜湊值。

進位制可以亂設,但儘量大於 \(N\) 並且最好為質數。

可以使用雙雜湊以提高正確率。

程式碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int ui;
const int N = 2e5 + 5;
int n, q, a[N], b[N];
ull ha[N], hb[N], pw[N];
ull getHA(int l, int r) {
    return ha[r] - ha[l - 1];
}
ull getHB(int l, int r) {
    return hb[r] - hb[l - 1];
}
ui ha2[N], hb2[N], pw2[N];
ui getHA2(int l, int r) {
    return ha2[r] - ha2[l - 1];
}
ui getHB2(int l, int r) {
    return hb2[r] - hb2[l - 1];
}
void solve() {
    cin >> n >> q;
    pw[0] = 1, pw2[0] = 1;
    for (int i = 1; i <= n; i ++) pw[i] = pw[i - 1] * 19260817;
    for (int i = 1; i <= n; i ++) pw2[i] = pw2[i - 1] * 998244353;
    for (int i = 1; i <= n; i ++) cin >> a[i];
    for (int i = 1; i <= n; i ++) cin >> b[i];
    for (int i = 1; i <= n; i ++) 
        ha[i] = ha[i - 1] + pw[a[i]],
        ha2[i] = ha2[i - 1] + pw2[a[i]];
    for (int i = 1; i <= n; i ++)
        hb[i] = hb[i - 1] + pw[b[i]],
        hb2[i] = hb2[i - 1] + pw2[b[i]];
    while (q --) {
        int l, r, L, R;
        cin >> l >> r >> L >> R;
        ull HA, HB;
        HA = getHA(l, r);
        HB = getHB(L, R);
        ui HA2, HB2;
        HA2 = getHA2(l, r);
        HB2 = getHB2(L, R);
        if (HA == HB && HA2 == HB2) cout << "Yes\n";
        else cout << "No\n";
    }
}
int main() {
    int T = 1;
    // cin >> T;
    while (T --) solve();
    return 0;
}