Atcoder ABC321 笔记

发布时间 2023-09-27 19:15:33作者: KevinLikesCoding

A - 321-like Checker \(\color{gray}{22}\)

直接模拟

void solve() {
    int n;
    cin >> n;
    int lst = -1;
    for(int i = n; i; i /= 10) {
        int u = i % 10;
        if(u <= lst) {
            cout << "No" << endl;
            return;
        }
        lst = u;
    }
    cout << "Yes" << endl;
}

B - Cutoff \(\color{gray}{220}\)

直接枚举分数,时间复杂度 \(O(n^2\log n)\)

void solve() {
    int n, x;
    cin >> n >> x;
    vector<int> a(n), b;
    REP(i, n - 1) cin >> a[i];
    FOR(i, 0, 100) {
        b = a;
        b[n - 1] = i;
        sort(ALL(b));
        int s = 0;
        FOR(i, 1, n - 2) s += b[i];
        if(s >= x) {
            cout << i << endl;
            return;
        }
    }
    cout << -1 << endl;
}

C - 321-like Searcher \(\color{brown}{591}\)

符合条件的数比较少,直接模拟。

void solve() {
    int k;
    cin >> k;
    vector<ll> a;
    REP(i, 1 << 10) {
        ll res = 0;
        ROF(j, 9, 0) {
            if(i >> j & 1) {
                res = res * 10 + j;
            }
        }
        a.push_back(res);
    }
    sort(ALL(a));
    cout << a[k + 1] << endl;
}

D - Set Menu \(\color{green}{806}\)

对于每一个数,会有可以直接加的和封顶的,
二分这个边界即可。

void solve() {
    int n, m, p;
    cin >> n >> m >> p;
    vector<int> a(n), b(m);
    REP(i, n) cin >> a[i];
    REP(i, m) cin >> b[i];
    sort(ALL(a));
    vector<ll> s(n + 1);
    FOR(i, 0, n - 1) s[i + 1] = a[i] + s[i];
    ll ans = 0;
    REP(i, m) {
        int u = lower_bound(ALL(a), p - b[i]) - BG(a);
        ans += s[u];
        ans += 1ll * u * b[i];
        ans += 1ll * p * (n - u);
    }
    cout << ans << endl;
}

E - Complete Binary Tree \(\color{blue}{1627}\)

枚举 lca,再二分距离为 \(k\) 的节点个数。

ll n, x, k;
ll calc(ll u, ll k) {
    if(u > n || k < 0) return 0;
    ll l = u, r = u;
    while(k--) {
        l <<= 1;
        r <<= 1; r |= 1;
        if(l > n) return 0;
    }
    if(r <= n) return r - l + 1;
    else return n - l + 1;
}
void solve() {
    cin >> n >> x >> k;
    ll lst = LINF << 1, ans = 0;
    while(x) {
        ans += calc(x, k);
        ans -= calc(lst, k - 1);
        k--;
        lst = x;
        x >>= 1;
    }    
    cout << ans << endl;
}

F - #(subset sum = K) with Add and Erase \(\color{blue}{1645}\)

第一种就是基本的背包
第二种直接倒过来操作即可

const int N = 5e3 + 5;
int q, k;
int f[N];
void solve() {
    cin >> q >> k;
    f[0] = 1;
    REP(ii, q) {
        string opt;
        int x;
        cin >> opt >> x;
        if(opt == "+") {
            ROF(i, N - 1, x) {
                f[i] = (f[i] + f[i - x]) % P;
            }
        }
        else {
            FOR(i, x, N - 1) {
                f[i] = (f[i] - f[i - x] + P) % P;
            }
        }
        cout << f[k] << endl;
    }
}

G - Electric Circuit \(\color{orange}{2439}\)

状压枚举不和其它点有连边的点集,
但是点集连变方案中有重复的,
需要枚举子集进行去重。

const int N = 17;
const int M = 1 << 17;
int n, m, ru[N], ch[N];
int sr[M], sc[M];
mint f[M], g[M], ans;
void solve() {
    cin >> n >> m;
    REP(i, m) {
        int x; cin >> x; x--;
        ru[x]++;
    }
    REP(i, m) {
        int x; cin >> x; x--;
        ch[x]++;
    }
    comb.init(m);
    FOR(i, 1, (1 << n) - 1) {
        REP(j, n) {
            if(i & (1 << j)) {
                sr[i] += ru[j];
                sc[i] += ch[j];
            }
        }
        if(sr[i] != sc[i]) continue;
        f[i] = g[i] = comb.fac[sr[i]];
        for(int j = i; j; j = (j - 1) & i) {
            if(j & (-i & i)) {
                f[i] -= f[j] * g[i ^ j];
            }
        }
        ans += f[i] * comb.fac[m - sr[i]];
    }
    cout << ans / comb.fac[m] << endl;
}