C. Large Queue

用队列维护二元组 (值,数量),本质上和ABC247D一样

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;

int main() {
    int Q;
    cin >> Q;
    
    queue<pair<int, int>> q;
    rep(i, Q) {
        int type;
        cin >> type;
        if (type == 1) {
            int c, x;
            cin >> c >> x;
            q.emplace(x, c);
        }
        else {
            int k;
            cin >> k;
            ll ans = 0;
            while (k) {
                auto [x, c] = q.front();
                if (c <= k) {
                    ans += (ll)x*c;
                    k -= c;
                    q.pop();
                }
                else {
                    ans += (ll)x*k;
                    q.front().second -= k;
                    k = 0;
                }
            }
            cout << ans << '\n';
        }
    }
    
    return 0;
}

D. Make Geometric Sequence

先将序列按绝对值大小做升序排序
然后利用等比数列的判定条件直接判定即可
由于这里的比值可能是分数,所以可以转化成乘法
还需注意需要特判所有数的绝对值都相同的情况:考虑正数和负数交叉排列,最后最多剩下一个整数或负数

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;

bool solve() {
    int n;
    cin >> n;
    
    vector<ll> a(n);
    rep(i, n) cin >> a[i];
    
    ranges::sort(a, [](ll x, ll y) { return abs(x) < abs(y); });
    
    {
        bool ok = true;
        rep(i, n-2) {
            if (a[i]*a[i+2] != a[i+1]*a[i+1]) ok = false;
        }
        if (ok) return true;
    }
    
    if (abs(a[0]) == abs(a.back())) {
        int pos = 0, neg = 0;
        rep(i, n) if (a[i] < 0) neg++; else pos++;
        if (abs(neg-pos) <= 1) return true;
    }
    
    return false;
}

int main() {
    int t;
    cin >> t;
    
    while (t--) {
        if (solve()) puts("Yes");
        else puts("No");
    }
    
    return 0;
}

E. Reverse 2^i

本题要求在不改变对战组合的前提下重排锦标赛对战表。关键在于:通过整体翻转和分段反转实现区间反转。
具体做法:

  1. 操作原理:
  • 将整个对战表完全反转
  • 再将前半段和后半段分别独立反转
  • 即可实现前半段和后半段的位置交换
  1. 最优策略:
  • 分别求出前半段和后半段的字典序最小排列
  • 将字典序较小的段放在前面

7.6

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

void solve() {
    int n;
    cin >> n;
    
    int n2 = 1<<n;
    vector<int> p(n2);
    rep(i, n2) cin >> p[i];
    
    for (int w = 1; w < n2; w <<= 1) {
        for (int l = 0; l < n2; l += w*2) {
            if (p[l] > p[l+w]) {
                rep(i, w) swap(p[l+i], p[l+i+w]);
            }
        }
    }
    
    rep(i, n2) cout << p[i] << " \n"[i == n2-1];
}

int main() {
    int t;
    cin >> t;
    
    while (t--) solve();
    
    return 0;
}

还有一种思路是看区间中的最小值在左半段还是右半段,如果在右半段,就将整个区间翻转

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

void solve() {
    int n;
    cin >> n;
    
    int n2 = 1<<n;
    vector<int> p(n2);
    rep(i, n2) cin >> p[i];
    
    auto f = [&](auto& f, int l, int r) {
        if (r-l == 1) return;
        int c = (l+r)/2;
        int minl = *min_element(p.begin()+l, p.begin()+c);
        int minr = *min_element(p.begin()+c, p.begin()+r);
        if (minl > minr) reverse(p.begin()+l, p.begin()+r);
        f(f, l, c);
        f(f, c, r);
    };
    f(f, 0, n2);
    
    rep(i, n2) cout << p[i] << " \n"[i == n2-1];
}

int main() {
    int t;
    cin >> t;
    
    while (t--) solve();
    
    return 0;
}

F. No Passage

在青木不干扰时,标准的bfs
在有干扰的情况下,需要改良bfs算法 —— 当某个点第二次被访问时执行以下操作:

  • 更新该点的最短距离
  • 重新将其加入队列
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;
using P = pair<int, int>;

const int di[] = {-1, 0, 1, 0};
const int dj[] = {0, 1, 0, -1};

int main() {
    int h, w, k;
    cin >> h >> w >> k;
    
    const int INF = 1001001001;
    vector dist(h, vector<int>(w, INF));
    vector checked(h, vector<bool>(w));
    queue<P> q;
    rep(i, k) {
        int r, c;
        cin >> r >> c;
        --r; --c;
        dist[r][c] = 0;
        q.emplace(r, c);
    }
    
    while (q.size()) {
        auto [i, j] = q.front(); q.pop();
        rep(v, 4) {
            int ni = i+di[v], nj = j+dj[v];
            if (ni < 0 or nj < 0 or ni >= h or nj >= w) continue;
            if (dist[ni][nj] != INF) continue;
            if (checked[ni][nj]) {
                dist[ni][nj] = dist[i][j]+1;
                q.emplace(ni, nj);
            }
            else checked[ni][nj] = true;
        }
    }
    
    ll ans = 0;
    rep(i, h)rep(j, w) if (dist[i][j] != INF) ans += dist[i][j];
    
    cout << ans << '\n';
    
    return 0;
}

G. Big Banned Grid

分割平面图,找一条路径将 \((1, 1)\)\((H, W)\) 分隔开即可
可以用bfs来实现

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using P = pair<int, int>;

int main() {
    int h, w, k;
    cin >> h >> w >> k;
    
    vector<P> ps;
    map<P, int> mp;
    rep(i, k) {
        int r, c;
        cin >> r >> c;
        ps.emplace_back(r, c);
        mp[ps[i]] = i;
    }
    
    vector<bool> used(k);
    queue<int> q;
    rep(i, k) {
        auto [r, c] = ps[i];
        if (r == 1 or c == w) {
            used[i] = true;
            q.push(i);
        }
    }
    while (q.size()) {
        int v = q.front(); q.pop();
        auto [i, j] = ps[v];
        for (int di = -1; di <= 1; ++di) {
            for (int dj = -1; dj <= 1; ++dj) {
                int ni = i+di, nj = j+dj;
                if (!mp.count(P(ni, nj))) continue;
                int u = mp[P(ni, nj)];
                if (used[u]) continue;
                used[u] = true;
                q.push(u);
            }
        }
    }
    
    rep(i, k) if (used[i]) {
        auto [r, c] = ps[i];
        if (r == h or c == 1) {
            puts("No");
            return 0;
        }
    }
    puts("Yes");
    
    return 0;
}