20250814

T1

偷金计划

求出每个点到最近守卫的距离,按这个从大到小加点建重构树,每次查询两点在重构树上 LCA 的点权即可。

代码
#include <iostream>
#include <algorithm>
#include <string.h>
#include <vector>
#include <queue>
#define int long long
using namespace std;
int n, m, K;
struct node {
    int x, dis;
} tmp;
bool operator<(node a, node b) { return a.dis > b.dis; }
priority_queue<node> q;
int dist[100005];
bool vis[100005];
int head[100005], nxt[400005], to[400005], ew[400005], ecnt;
void add(int u, int v, int ww) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt, ew[ecnt] = ww; }
void dijkstra() {
    while (!q.empty()) {
        tmp = q.top();
        q.pop();
        int x = tmp.x;
        if (vis[x]) 
            continue;
        vis[x] = 1;
        for (int i = head[x]; i; i = nxt[i]) {
            int v = to[i];
            if (dist[v] > dist[x] + ew[i]) 
                q.push((node) { v, dist[v] = dist[x] + ew[i] });
        }
    }
}
int ord[100005];
int dsu[100005];
int getf(int x) { return (dsu[x] == x ? x : (dsu[x] = getf(dsu[x]))); }
int son[100005], dep[100005], top[100005], sz[100005], f[100005];
vector<int> G[100005];
void dfs1(int x, int fa, int d) {
    dep[x] = d;
    f[x] = fa;
    sz[x] = 1;
    for (int v : G[x]) {
        dfs1(v, x, d + 1);
        sz[x] += sz[v];
        if (sz[v] > sz[son[x]]) 
            son[x] = v;
    }
}
void dfs2(int x, int t) {
    top[x] = t;
    if (!son[x]) return;
    dfs2(son[x], t);
    for (int v : G[x]) {
        if (v != son[x]) dfs2(v, v);
    }
}
int LCA(int x, int y) {
    while (top[x] ^ top[y]) (dep[top[x]] < dep[top[y]]) ? (y = f[top[y]]) : (x = f[top[x]]);
    return dep[x] < dep[y] ? x : y;
}
bool chk[100005];
signed main() {
    freopen("gold.in", "r", stdin);
    freopen("gold.out", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int u, v, ww;
        cin >> u >> v >> ww;
        add(u, v, ww);
        add(v, u, ww);
    }
    cin >> K;
    memset(dist, 63, sizeof dist);
    for (int i = 1, x; i <= K; i++) cin >> x, q.push((node) { x, dist[x] = 0 });
    dijkstra();
    for (int i = 1; i <= n; i++) dsu[i] = ord[i] = i;
    sort(ord + 1, ord + n + 1, [](int x, int y) { return dist[x] > dist[y]; });
    for (int i = 1; i <= n; i++) {
        int x = ord[i];
        for (int j = head[x]; j; j = nxt[j]) {
            int v = to[j];
            if (chk[v] && getf(v) != x) {
                G[x].emplace_back(getf(v));
                dsu[getf(v)] = x;
            }
        }
        chk[x] = 1;
    }
    dfs1(ord[n], 0, 1);
    dfs2(ord[n], ord[n]);
    int _;
    cin >> _;
    while (_--) {
        int x, y;
        cin >> x >> y;
        cout << max(0ll, dist[LCA(x, y)] - 1) << "\n";
    }
    return 0;
}

T2

武神宫考题

笛卡尔树上启发式合并,发现修改查询复杂度不平衡,考虑每个数分高 \(7\) 位和低 \(7\) 位,高位枚举子集查询,低位枚举超集修改,总复杂度 \(\mathcal{O}(n\log n\sqrt{V})\)。跑得飞快。

代码
#include <iostream>
#define int long long
using namespace std;
int n;
int o[100005];
int a[100005];
int mx[20][100005];
inline int cmax(int x, int y) { return o[x] < o[y] ? y : x; }
int qmax(int l, int r) {
    int k = 63 - __builtin_clzll(r - l + 1);
    return cmax(mx[k][l], mx[k][r - (1 << k) + 1]);
}
int val[100005], cnt[100005];
void add(int x, int y) {
    cnt[x] += y;
    int up = (x >> 7), dw = (x & 127);
    for (int i = dw; i < 127; i = (i + 1) | dw) val[up << 7 | i] += y;
    val[up << 7 | 127] += y;
}
int ans = 0, delsame;
int query(int x) {
    int up = (x >> 7), dw = (x & 127), ret = val[dw];
    for (int i = up; i; i = (i - 1) & up) ret += val[i << 7 | dw];
    return ret - delsame * cnt[x];
}
void Solve(int l, int r) {
    if (l > r) return;
    int mid = qmax(l, r), p = mid;
    if (mid - l <= r - mid) {
        Solve(l, mid - 1);
        for (int i = l; i < mid; i++) add(a[i], -1);
        Solve(mid + 1, r);
        for (int i = l; i < mid; i++) ans += o[p] * query(a[i]);
        for (int i = l; i < mid; i++) add(a[i], 1);
    } else {
        Solve(mid + 1, r);
        for (int i = mid + 1; i <= r; i++) add(a[i], -1);
        Solve(l, mid - 1);
        for (int i = mid + 1; i <= r; i++) ans += o[p] * query(a[i]);
        for (int i = mid + 1; i <= r; i++) add(a[i], 1);
    }
    ans += o[p] * query(a[p]);
    add(a[p], 1);
}
signed main() {
    freopen("problem.in", "r", stdin);
    freopen("problem.out", "w", stdout);
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i], o[i] = a[i], mx[0][i] = i;
    for (int i = 1; (1 << i) <= n; i++) {
        for (int j = 1; j + (1 << i) - 1 <= n; j++) 
            mx[i][j] = cmax(mx[i - 1][j], mx[i - 1][j + (1 << (i - 1))]);
    }
    Solve(1, n);
    for (int i = 1; i <= n; i++) add(a[i], -1), a[i] = 16383 ^ a[i];
    delsame = 1; Solve(1, n);
    cout << ans << "\n";
    return 0;
}

T3

费用流板子。源向限制连边,限制向魔法球连边,魔法球向汇连一系列边,费用为代价的差分。以上所有边流量均为 \(1\)。最后跑最小费用最大流即可。

代码
#include <iostream>
#include <string.h>
#include <queue>
// #define int long long
using namespace std;
const int inf = 2147483647;
int cn, m, P;
int t[45][105];
struct Cost_Flow {
    static const int N = 100005, M = 8000005;
    int n, S, T, c;
    int head[N], cur[N], nxt[M], to[M], cst[M], res[M], ecnt;
    int add(int u, int v, int x, int y) {
        to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt, cst[ecnt] = y, res[ecnt] = x;
        to[++ecnt] = u, nxt[ecnt] = head[v], head[v] = ecnt, cst[ecnt] = -y, res[ecnt] = 0;
        return ecnt - 1;
    }
    void init(int nn, int ss, int tt) {
        n = nn, S = ss, T = tt;
        ecnt = 1, c = 0;
        for (int i = 0; i <= n; i++) head[i] = 0;
    }
    int dist[N];
    bool inq[N], vis[N];
    queue<int> q;
    bool spfa() { // min cost
        q.push(S);
        memset(dist, 63, sizeof dist);
        dist[S] = 0;
        while (!q.empty()) {
            int x = q.front();
            q.pop();
            inq[x] = 0;
            for (int i = head[x]; i; i = nxt[i]) {
                int v = to[i];
                if (dist[v] > dist[x] + cst[i] && res[i]) {
                    dist[v] = dist[x] + cst[i];
                    if (!inq[v]) {
                        inq[v] = 1;
                        q.push(v);
                    }
                }
            }
        }
        return (dist[T] != dist[0]);
    }
    int dfs(int x, int flow) {
        if (x == T) 
            return flow;
        vis[x] = 1;
        int ret = 0;
        for (int i = cur[x]; i && flow; i = nxt[i]) {
            cur[x] = i;
            int v = to[i];
            if (dist[v] == dist[x] + cst[i] && res[i] && !vis[v]) {
                int tmp = dfs(v, min(flow, res[i]));
                if (tmp) {
                    res[i] -= tmp;
                    res[i ^ 1] += tmp;
                    flow -= tmp;
                    ret += tmp;
                    c += tmp * cst[i];
                }
            }
        }
        if (!ret) 
            dist[x] = inf;
        vis[x] = 0;
        return ret;
    }
    int dinic() {
        int ret = 0;
        while (spfa()) {
            for (int i = 1; i <= n; i++) cur[i] = head[i];
            ret += dfs(S, inf);
        }
        return ret;
    }
} G;
int n, a, b, c;
int id[85][85], ncnt;
signed main() {
    freopen("shadow.in", "r", stdin);
    freopen("shadow.out", "w", stdout);
    cin >> n >> a >> b >> c;
    ncnt = n;
    G.init(n + n * (n + 1) / 2 + 2, n + n * (n + 1) / 2 + 1, n + n * (n + 1) / 2 + 2);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= i; j++) {
            id[i][j] = ++ncnt;
            int x;
            cin >> x;
            if (x != 1) G.add(ncnt, i, 1, 0);
            if (x != -1) G.add(ncnt, j, 1, 0);
            G.add(G.S, ncnt, 1, 0);
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) 
            G.add(i, G.T, 1, (2 * j - 1) * a + b);
    }
    G.dinic();
    cout << G.c + n * c << "\n";
    return 0;
}

T4

最大流和哈密顿

建出最小割树,则变为选择一个圆排列,最小化 相邻两个节点在树上的最小边权 的和。显然可以通过每次删掉最小边分成两个连通块分别构造再拼起来做到只有最小边被算两次,其他边只算一次。于是就做完了。

最小割树

此题好像是等价流树(限制更弱)。建树方法为:初始点集为全集,每次在当前点集中任选两个点作为 \(s, t\),求出原图中(包含原图中所有点所有边)\(s\)\(t\) 的最小割,然后树上这两个点间就连边权为最小割大小的边。然后把这个最小割的两个割集求出来,向两个割集分别递归建树即可。递归边界是当前集合只有一个点。

代码
#include <iostream>
#include <algorithm>
#include <string.h>
#include <vector>
#include <queue>
#define int long long
using namespace std;
const int inf = 0x7fffffff, INF = 0x3f3f3f3f3f3f3f3f;
const int N = 200005, M = 2000005;
struct Flow {
    int n, m, S, T;
    int head[N + 5], nxt[M + 5], to[M + 5], res[M + 5], ecnt = 1;
    int cur[N + 5];
    int add(int u, int v, int ww) {
        to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt, res[ecnt] = ww;
        to[++ecnt] = u, nxt[ecnt] = head[v], head[v] = ecnt, res[ecnt] = 0;
        return ecnt;
    }
    int dep[N + 5];
    queue<int> q;
    void init(int nn, int s, int t) {
        n = nn, S = s, T = t, ecnt = 1;
        for (int i = 0; i <= n; i++) head[i] = 0;
    }
    bool bfs(int s, int t) {
        for (int i = 1; i <= n; i++) dep[i] = 0;
        q.push(s);
        dep[s] = 1;
        while (!q.empty()) {
            int x = q.front();
            q.pop();
            for (int i = head[x]; i; i = nxt[i]) {
                int v = to[i];
                if (!dep[v] && res[i] > 0) {
                    dep[v] = dep[x] + 1;
                    q.push(v);
                }
            }
        }
        return (dep[t] > 0);
    }
    int dfs(int x, int flow) {
        if (x == T || !flow) 
            return flow;
        int ret = 0;
        for (int i = cur[x]; i && flow; i = nxt[i]) {
            cur[x] = i;
            int v = to[i];
            if (dep[v] == dep[x] + 1 && res[i] > 0) {
                int tmp = dfs(v, min(flow, res[i]));
                res[i] -= tmp;
                res[i ^ 1] += tmp;
                ret += tmp;
                flow -= tmp;
            }
        }
        if (!ret) 
            dep[x] = 0;
        return ret;
    }
    int MaxFlow(int ss, int tt) {
        S = ss, T = tt;
        int ret = 0;
        while (bfs(S, T)) {
            for (int i = 1; i <= n; i++) cur[i] = head[i];
            ret += dfs(S, inf);
        }
        return ret;
    }
    void Re() { for (int i = 2; i <= ecnt; i += 2) res[i] += res[i ^ 1], res[i ^ 1] = 0; }
} g;
int n, m;
struct Edge { int u, v, w; } e[1505], et[1505];
int G[505][505];
int f[20][300005], fr[20][300005];
int p[15];
int head[505], nxt[1005], to[1005], ew[1005], ecnt;
void add(int u, int v, int ww) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt, ew[ecnt] = ww; }
bool vis[505];
void dfs(int x) {
    vis[x] = 1;
    for (int i = g.head[x], v; i; i = g.nxt[i]) if (g.res[i] && !vis[v = g.to[i]]) dfs(v);
}
int cnt;
void dfs(vector<int> vec) {
    if ((int)vec.size() == 1) return;
    int s = vec[0], t = vec[1], val = g.MaxFlow(s, t);
    add(s, t, val), add(t, s, val);
    et[++cnt] = (Edge) { s, t, val };
    memset(vis, 0, sizeof vis);
    dfs(s);
    vector<int> vs, vt;
    for (int v : vec) vis[v] ? vs.emplace_back(v) : vt.emplace_back(v);
    g.Re();
    dfs(vs), dfs(vt);
}
void dfs(int x, int fa, int X, int cur) {
    G[x][X] = cur;
    for (int i = head[x]; i; i = nxt[i]) {
        int v = to[i];
        if (v != fa) dfs(v, x, X, min(cur, ew[i]));
    }
}
bool in[505];
int ansp[505], sz;
void dfs_(int x, vector<int> &vec) {
    in[x] = 0;
    vec.emplace_back(x);
    for (int i = head[x]; i; i = nxt[i]) {
        int v = to[i];
        if (in[v]) dfs_(v, vec);
    }
    in[x] = 1;
}
void dfs1(vector<int> vec) {
    if ((int)vec.size() == 1) return ansp[++sz] = vec[0], void();
    memset(in, 0, sizeof in);
    for (int v : vec) in[v] = 1;
    vector<int> vl, vr;
    for (int i = 1; i <= cnt; i++) {
        if (in[et[i].u] && in[et[i].v]) {
            in[et[i].v] = 0; dfs_(et[i].u, vl); in[et[i].v] = 1;
            in[et[i].u] = 0; dfs_(et[i].v, vr); in[et[i].u] = 1;
            dfs1(vl), dfs1(vr);
            break;
        }
    }
}
signed main() {
    freopen("b.in", "r", stdin);
    freopen("b.out", "w", stdout);
    cin >> n >> m;
    g.init(n, 0, 0);
    for (int i = 1; i <= m; i++) cin >> e[i].u >> e[i].v >> e[i].w;
    for (int i = 1; i <= m; i++) g.add(e[i].u, e[i].v, e[i].w), g.add(e[i].v, e[i].u, e[i].w);
    vector<int> U;
    for (int i = 1; i <= n; i++) U.emplace_back(i);
    dfs(U);
    for (int i = 1; i <= n; i++) dfs(i, 0, i, inf);
    sort(et + 1, et + cnt + 1, [](Edge a, Edge b) { return a.w < b.w; });
    dfs1(U);
    int ans = 0;
    for (int i = 1; i <= n; i++) ans += G[ansp[i]][ansp[i % n + 1]];
    cout << ans << "\n";
    for (int i = 1; i <= n; i++) cout << ansp[i] << " ";
    cout << "\n";
    return 0;
}

平衡思想。

费用流。

最小割树。

posted @ 2025-08-14 23:23  forgotmyhandle  阅读(25)  评论(0)    收藏  举报