某岛

… : "…アッカリ~ン . .. . " .. .
April 26, 2023

AtCoder Beginner Contest 298

Problem F. Rook Score

我们记录每一行和每一列的和 R[], C[],那么答案是某个 R[] + C[] – X[][]
注意这里 X[][] 可能在输入里,也可能不在,前者只有 O(n) 种,
后者对每一个 R,我们只要找其中最大的 C,所以其实也只有 O(n) 种。

排序后二重循环加个剪枝即可。

#include <lastweapon/io>
using namespace lastweapon;
const int N = int(2e5) + 9;
set<PII> P; map<int, LL> R, C; vector<pair<LL, int>> RR, CC;
int r[N], c[N], x[N];
int n;

int main() {

#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    //freopen("/Users/minakokojima/Documents/GitHub/ACM-Training/Workspace/out.txt", "w", stdout);
#endif

    RD(n); REP(i, n) {
        RD(r[i], c[i], x[i]);
        R[r[i]] += x[i];
        C[c[i]] += x[i];
        P.insert({r[i], c[i]});
    }

    for (auto r: R) RR.PB({r.se, r.fi});
    for (auto c: C) CC.PB({c.se, c.fi});
    SRT(RR); SRT(CC); RVS(RR); RVS(CC);

    LL z = 0; REP(i, n) checkMax(z, R[r[i]] + C[c[i]] - x[i]);

    REP(i, SZ(RR)) REP(j, SZ(CC)) {
        LL zz = RR[i].fi + CC[j].fi; if (zz <= z) break;
        if (!CTN(P, MP(RR[i].se, CC[j].se))) z = zz;
    }

    cout << z << endl;
}

Problem Ex. Sum of Min of Length

核心还是倍增祖先求出 x, y 两点之间的中点,然后还得根据 lca 的情况各种分类讨论。虽然之前我们学会了 dfs 序求 lca 这样的高级知识,但看起来这个题看起来避免不了倍增祖先,所以还是用倍增祖先求 lca 吧!

首先还是 dfs 各种预处理,这里还要需要两次 dfs,因为需要跑树形 dp 求出 dn[x] 和 up[x],分别表示向下、向上的距离和。

根据路径的奇偶性,中点可能在两点之间也可能恰好落在某个点上。
对于那样的情况,它任意连向 x 或者 y 都可,但是这样讨论的话感觉情况有点多压力有点大…

于是我们需要设计一个子函数 ff(x, y),表示求出所有到 x 的但不经过 y 的路径和!
这样的话无论如何我们都可以求两个分割点,避免讨论路径的奇偶性,而是只要处理一下两边深度一样的情况,压力大幅减轻!

最后只要考虑怎么求 ff(x, y),有三种情况,无论哪一种我们都先加上 dn[x]+up[x]。
第一种是 x 是 y 的祖先,那么不仅要减去 dn[y],并且 y 子树的所有点还要继续减去少算的距离,也就是 sz[y] * (dep[y]-dep[x])。
第二种是 y 是 x 的祖先,此时要先求出 y 一圈但不包含 x 所在分支的和,我们需要先求出 y 的孩子中是 x 祖先的节点 w,那么就是减去 dn[y]-dn[w]-sz[w],然后再继续减去少算的,也就是 (LL)(n-sz[w]) * (dep[x]-dep[y])。
最后一种情况是有另一个 lca,这种情况可以合并到情况一,只要把距离函数改成 (dep[x]+dep[y]-2*dep[l]) 即可。

#include <lastweapon/io>
using namespace lastweapon;
const int N = int(2e5) + 9, LV = 20;
int fa[LV][N], dep[N], sz[N]; LL dn[N], up[N];
VI adj[N]; int n;

int move_up(int x, int d) {
    for (int lv=0;d;++lv,d>>=1)
        if (d&1) x = fa[lv][x];
    return x;
}
inline int lca(int x, int y) {
    if (dep[y] < dep[x]) swap(x, y); y = move_up(y, dep[y] - dep[x]); if (x == y) return x;
    DWN(lv, LV, 0) if (fa[lv][x] != fa[lv][y]) x = fa[lv][x], y = fa[lv][y];
    return fa[0][x];
}
void dfs1(int u = 1, int p = 0) {
    sz[u] = 1;
    for (auto v: adj[u]) if (v != p) {
        dep[v] = dep[u] + 1;
        fa[0][v] = u; for (int lv=0;fa[lv+1][v]=fa[lv][fa[lv][v]];++lv);
        dfs1(v, u);
        sz[u] += sz[v];
        dn[u] += dn[v] + sz[v];
    }
}
void dfs2(int u = 1, int p = 0) {
    for (auto v: adj[u]) if (v != p) {
        up[v] = up[u] + dn[u] + n - dn[v] - 2*sz[v];
        dfs2(v, u);
    }
}
int sub(int u, int v) {
    return move_up(v, dep[v]-dep[u]-1);
}

LL ff(int x, int y) {
    // y
    // x
    // z    x
    //x y   y
    LL z = dn[x] + up[x]; int l = lca(x, y);
    if (l == y) {
        int w = sub(y, x);
        z -= up[y] + (dn[y] - dn[w] - sz[w]) + (LL)(n-sz[w])*(dep[x]-dep[y]);
    } else {
        z -= dn[y] + (LL)sz[y]*(dep[x]+dep[y]-2*dep[l]);
    }
    return z;
}

//    1
//  6 4 8
// 75 2
// 3

LL f(int a, int b) {

    if (a == b) return dn[a] + up[a];
    if (dep[a] < dep[b]) swap(a, b);
    int l = lca(a, b), d = dep[a] + dep[b] - 2*dep[l];
    int ma = move_up(a, d/2), mb = ma == l ? move_up(b, d/2-1) : fa[0][ma];
    LL z = ff(a, mb) + ff(b, ma);
    return z;
}

int main() {

#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    //freopen("/Users/minakokojima/Documents/GitHub/ACM-Training/Workspace/out.txt", "w", stdout);
#endif

    RD(n); DO(n-1) {
        int x, y; RD(x, y);
        adj[x].PB(y);
        adj[y].PB(x);
    }

    dfs1(); dfs2();

    Rush {
        int a, b; RD(a, b);
        printf("%lld\n", f(a, b));
    }
}