C. On a Diet

维护一下长度为 \(M\) 的滑动窗口里吃掉的零食的总热量

代码实现
#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 n, m; ll k;
    cin >> n >> m >> k;
    
    vector<int> a(n);
    rep(i, n) cin >> a[i];
    
    vector<bool> eat(n);
    ll sum = 0;
    rep(i, n) {
        if (i >= m and eat[i-m]) sum -= a[i-m]; 
        sum += a[i];
        if (sum <= k) {
            eat[i] = true;
        }
        else {
            sum -= a[i];
            eat[i] = false;
        }
    }
    
    rep(i, n) {
        if (eat[i]) puts("Yes");
        else puts("No");
    }
    
    return 0;
}

D. Bomber Mad

以所有的安全空格子为起点跑多源 \(\text{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 di[] = {-1, 0, 1, 0};
int dj[] = {0, -1, 0, 1};

int main() {
    int h, w, k;
    cin >> h >> w >> k;
    
    vector<string> s(h);
    rep(i, h) cin >> s[i];
    
    vector<bool> row(h), col(w);
    rep(i, h)rep(j, w) {
        if (s[i][j] == '#') row[i] = col[j] = true;
    }
    
    const int INF = 1001001001;
    vector dist(h, vector<int>(w, INF));
    queue<P> q;
    auto push = [&](int i, int j, int d) {
        if (dist[i][j] != INF) return;
        dist[i][j] = d;
        q.emplace(i, j);
    };
    rep(i, h)rep(j, w) {
        if (!row[i] and !col[j]) push(i, j, 0);
    }
    
    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 (s[ni][nj] == '#') continue;
            push(ni, nj, dist[i][j]+1);
        }
    }
    
    int ans = 0;
    rep(i, h)rep(j, w) if (dist[i][j] <= k) ++ans;
    
    cout << ans << '\n';
    
    return 0;
}

E. Odd Cycle

二分图判定

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

using namespace std;

void solve() {
    int n, m;
    cin >> n >> m;
    
    vector<vector<int>> to(n);
    rep(i, m) {
        int a, b;
        cin >> a >> b;
        --a; --b;
        to[a].push_back(b);
        to[b].push_back(a);
    }
    
    vector<int> col(n, -1);
    vector<int> vs;
    auto f = [&](auto& f, int v, int c) -> bool {
        if (col[v] != -1) {
            if (col[v] != c) {
                reverse(vs.begin(), vs.end());
                while (vs.back() != v) vs.pop_back();
                return true;
            }
            return false;
        }
        vs.push_back(v);
        col[v] = c;
        for (int u : to[v]) {
            if (f(f, u, !c)) return true;
        }
        vs.pop_back();
        return false;
    };
    f(f, 0, 0);
    
    if (vs.size()) {
        cout << vs.size() << '\n';
        for (int v : vs) cout << v+1 << ' ';
        cout << '\n';
    }
    else {
        cout << -1 << '\n';
    }
}

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

F. Centroid of a Slice

对顶点按顺序构成的简单多边形,设 \(a=\sum\limits_i p_i \times p_{i+1}\)(这里 \(×\) 表示向量叉积),则多边形有向面积 \(A=\frac{1}{2}a\)

重心的分子可用边贡献累加得到:

\[\sum_i (p_i+p_{i+1})(p_i \times p_{i+1}) \]

最终重心为

\[C = \frac{1}{6A}\sum_i (p_i+p_{i+1})(p_i \times p_{i+1}) \]

由于 \(6A=3a\),如果用 \(a=\sum p_i×p_{i+1}\) ,则最后除以 \(3a\)

然后预处理一下前缀和即可。

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

using namespace std;
using ll = long long;

struct V {
    ll x, y;
    V(ll x=0, ll y=0): x(x), y(y) {}
    V& operator+=(const V& v) { x += v.x; y += v.y; return *this; }
    V operator+(const V& v) const { return V(*this) += v; }
    V& operator-=(const V& v) { x -= v.x; y -= v.y; return *this; }
    V operator-(const V& v) const { return V(*this) -= v; }
    V& operator*=(ll s) { x *= s; y *= s; return *this; }
    V operator*(ll s) const { return V(*this) *= s; }
    ll cross(const V& v) const { return x*v.y - v.x*y; }
};
istream& operator>>(istream& is, V& v) {
    is >> v.x >> v.y; return is;
}
ostream& operator<<(ostream& os, const V& v) {
    os << "(" << v.x << "," << v.y << ")"; return os;
}

int main() {
    int n, q;
    cin >> n >> q;
    
    vector<V> ps(n);
    rep(i, n) cin >> ps[i];
    rep(i, n) ps.push_back(ps[i]);
    
    int n2 = n*2;
    vector<ll> area(n2);
    vector<V> cent(n2);
    rep(i, n2-1) {
        V p = ps[i], q = ps[i+1];
        ll a = p.cross(q);
        V c = p+q; c *= a;
        area[i+1] = area[i] + a;
        cent[i+1] = cent[i] + c;
    }
    
    rep(qi, q) {
        int l, r;
        cin >> l >> r;
        --l; --r;
        if (r < l) r += n;
        ll a = area[r]-area[l];
        V c = cent[r]-cent[l];
        {
            V p = ps[r], q = ps[l];
            ll na = p.cross(q);
            a += na;
            c += (p+q)*na;
        }
        double x = c.x, y = c.y;
        x /= a*3; y /= a*3;
        printf("%.10f %.10f\n", x, y);
    }
    
    return 0;
}

G. Cascading Grid

把每个非 # 的格子看作图中的一个节点,节点的权值为:若是 + 则收益 \(+1\),若是 - 则收益 \(-1\)

选择某个格子并把它能到达的所有格子变成 # 的约束,等价于:一旦在选删除集里包含某个节点 \(u\),那么图中从 \(u\) 出发能到达的所有节点 \(v\) 也必须被删除。也就是删除集要对有向边的「可达性」向前闭合(如果 \(u\) 在删除集,且有边 \(u \to v\),则 \(v\) 也必须在删除集)。

这正是标准的最大权闭合子图问题:我们要选一个闭合(对有向边前向闭合)的点集,使得保留的点权和最大化。等价地,删除的点集(补集)要最小化被删去的正贡献减去删去的负贡献,能用最小割建模。

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

using namespace std;

int main() {
    int h, w;
    cin >> h >> w;
    
    vector<string> s(h);
    rep(i, h) cin >> s[i];
    
    const int INF = 1001001001;
    int n = h*w;
    int sv = n, tv = sv+1;
    mf_graph<int> g(tv+1);
    auto f = [&](int i, int j) { return i*w+j; };
    
    int ans = 0;
    rep(i, h)rep(j, w) if (s[i][j] != '#') {
        if (s[i][j] == '+') ans++, g.add_edge(f(i, j), tv, 1);
        else g.add_edge(sv, f(i, j), 1);
        if (i and s[i-1][j] != '#') g.add_edge(f(i-1, j), f(i, j), INF);
        if (j and s[i][j-1] != '#') {
            g.add_edge(f(i, j-1), f(i, j), INF);
            g.add_edge(f(i, j), f(i, j-1), INF);
        }
    }
    ans -= g.flow(sv, tv);
    
    cout << ans << '\n';
    
    return 0;
}