[ABC348] Atcoder ABC 248 A~G 题解

[ABC348] Atcoder ABC 248 A~G 题解

A

模拟

B

模拟,不卡精度。

C

模拟

D

注意,药不可以拿着,只可以在那个格子吃掉。

这就意味着,我们无论何时到达某个点,到达的点的集合都是固定的。

所以对于每个药店跑 BFS,然后看起点到终点是否连通即可。

int n, m, k, ad[N][N], f[N][N], in[N][N], tox[N], toy[N];
int tx, ty, sx, sy;
char g[N][N];
bool st[N][N];
bool check(int x, int y) {
    return x > 0 && y > 0 && x <= n && y <= m && g[x][y] != '#';
}
int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};
vector<int> G[N];
void add(int a, int b) {
    if(a ^ b) G[a].push_back(b);
}
void dfs(int rt, int i, int j, int d) {
    queue<pair<int, int> > q;
    memset(st, 0, sizeof st);
    memset(f, 0x3f, sizeof f);
    f[i][j] = 0, q.push({i, j});
    while(q.size()) {
        auto [x, y] = q.front(); q.pop();
        if(st[x][y]) continue;
        st[x][y] = 1;
        for(int k = 0; k < 4; k ++) {
            int xx = x + dx[k], yy = y + dy[k];
            if(!check(xx, yy)) continue;
            if(f[xx][yy] > f[x][y] + 1) {
                f[xx][yy] = f[x][y] + 1;
                q.push({xx, yy});
            }
        }
    }
    for(int i = 1; i <= k; i ++)
        if(f[tox[i]][toy[i]] <= d)
            add(rt, i);
    if(f[tx][ty] <= d) add(rt, k + 1);
}
bool find(int u) {
    if(st[0][u]) return 0; st[0][u] = 1;
    if(u == k + 1) return 1;
    for(auto v : G[u])
        if(find(v)) return 1;
    return 0;
}

void work() {
    cin >> n >> m;
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++)
            cin >> g[i][j];
    cin >> k;
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++) {
            if(g[i][j] == 'S') sx = i, sy = j;
            else if(g[i][j] == 'T') tx = i, ty = j;
        }
    for(int i = 1; i <= k; i ++) {
        int x, y, d;
        cin >> x >> y >> d;
        ad[x][y] = d, in[x][y] = i;
        tox[i] = x, toy[i] = y;
    }
    if(!ad[sx][sy]) {
        cout << "No\n";
        return ;
    }
    for(int i = 1; i <= k; i ++)
        dfs(i, tox[i], toy[i], ad[tox[i]][toy[i]]);
    memset(st, 0, sizeof st);
    if(find(in[sx][sy])) cout << "Yes\n";
    else cout << "No\n";
}

E

【模板】换根 DP。

ans = 1e18

F

暴力题。

考虑 \(f_{i, j, k}\) 表示前 \(i\) 列,第 \(i\) 行与第 \(j\) 行是否合法。

转移即为:

\[f_{i, a, b}\to f_{i + 1, a, b}\oplus [g_{i, a} = g_{i, b}] \]

这个很 bitset,所以用 bitset 维护每一列的颜色情况,然后滚掉一维即可通过。

bitset<N> f[N], c[1000][N];
int n, m, a[N][N];

signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    for(int i = 1; i <= n; i ++) for(int j = 1; j <= m; j ++) cin >> a[i][j], c[a[i][j]][j][i] = 1;
    for(int i = 1; i <= m; i ++)
        for(int j = 1; j <= n; j ++)
            f[j] = f[j] ^ c[a[j][i]][i];
    int ans = 0;
    for(int i = 1; i <= n; i ++)
        ans += f[i].count() - f[i][i];
    cout << ans / 2 << '\n'; 

    return 0;
}

G

喵喵题,可惜也是重题。

如果只有一个 \(k\),可以固定 \(\max\) 然后就是一个查询 \(b_i\le \max\) 的前 \(k\) 大数和,明显可以主席树+线段树二分搞(好像有更简单的解法,等其他大佬的题解)。

观察到决策单调性:

对于选 \(k\) 个元素,设 \(f(k, i)\) 表示选 \(k\) 个,\(\max = i\) 时的答案,\(g(k)\) 表示选 \(k\) 个的最佳决策点。

我们有 \(i < j\wedge f(k, i)\le f(k, j) \Longrightarrow f(k+ \Delta, i)\le f(k+ \Delta, j), \Delta\ge 0\),这是因为 \(i < j\),所以 \(j\) 选择的位置包含了 \(i\) 可选的位置,所以合法的增加选的个数之后,\(j\) 一定可以选 \(i\) 选的,甚至可以选比它更优的位置。

所以决策单调性分治即可解决问题。

需要注意离散化。

时间复杂度:\(O(n\log^2n)\)

int n, f[N], disc[N], tot;
int find(int x) {
    return lower_bound(disc + 1, disc + tot + 1, x) - disc;
}
struct qwq {
    int a, b;
    bool operator < (const qwq &W) const {
        return b < W.b;
    }
} p[N];

int dat[M], ls[M], rs[M], cnt[M], idx, root[N];
#define mid (l + r >> 1)
int update(int q, int l, int r, int x, int v) {
    int p = ++ idx;
    ls[p] = ls[q], rs[p] = rs[q], cnt[p] = cnt[q] + 1, dat[p] = dat[q] + v;
    if(l == r) return p;
    if(x <= mid) ls[p] = update(ls[q], l, mid, x, v);
    else rs[p] = update(rs[q], mid + 1, r, x, v);
    return p;
}
int query(int q, int l, int r, int k) {
    if(l == r) return disc[l] * k;
    if(cnt[rs[q]] < k) return query(ls[q], l, mid, k - cnt[rs[q]]) + dat[rs[q]];
    return query(rs[q], mid + 1, r, k);
}
#undef mid
int g[N];
void solve(int l, int r, int ql, int qr) {
    if(l > r || ql > qr) return ;
    int mid = ql + qr >> 1, mid2 = -1, mx = -9e18;
    // find best point for mid
    for(int i = max(mid, l), t; i <= r; i ++) {
        t = query(root[i], 1, tot, mid) - p[i].b;
        if(t > mx) mx = t, mid2 = i;
    }
    f[mid] = mx;
    
    solve(l, mid2, ql, mid - 1);
    solve(mid2, r, mid + 1, qr);
}

signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n;
    for(int i = 1; i <= n; i ++) cin >> p[i].a >> p[i].b, disc[++ tot] = p[i].a;
    sort(p + 1, p + n + 1);
    sort(disc + 1, disc + tot + 1);
    tot = unique(disc + 1, disc + tot + 1) - disc;
    for(int i = 1; i <= n; i ++)
        root[i] = update(root[i - 1], 1, tot, find(p[i].a), p[i].a);
    solve(1, n, 1, n);
    for(int i = 1; i <= n; i ++) cout << f[i] << '\n';

    return 0;
}

总结

爆了,卡 D 了,看错题了,非常尴尬。

F 想到做法了但是没敢写。

posted @ 2024-04-09 22:30  MoyouSayuki  阅读(54)  评论(0编辑  收藏  举报
:name :name