LOJ#2587[APIO2018]Duathlon铁人两项(圆方树)

题目链接

洛谷

LOJ

前置知识

圆方树

解析

考虑一条从\(s\)\(f\)的路径产生的贡献是除\(s, f\)外经过的点数

如果这条路径经过了某个点双连通分量,点双上的每个点都会产生贡献

点双\(\rightarrow\)圆方树

建出圆方树,方点权值为代表的点双的大小

这样两点间的路径对应成两个圆点间的路径,由于一条路径会经过\(s\)\(f\)自己所在的点双,而\(c\)不能在\(s\)\(f\),所以每个圆点权值为\(-1\)

于是一对\(s, f\)的贡献就是圆方树上的路径上的点权和

考虑每个点被多少条路径经过,乘上点权就是这个点对答案的贡献

于是就变成了一个简单的树上路径统计问题

扫描二维码关注公众号,回复: 5690021 查看本文章

因为交换\(s\)\(f\)算不同的方案,最后答案乘\(2\)

代码

自带大常数……洛谷\(1000ms\)\(LOJ \ 5600ms\)……

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#define MAXN 100005
#define MAXM 200005

typedef long long LL;
struct Graph {
    struct Edge {
        int v, next;
        Edge(int _v = 0, int _n = 0):v(_v), next(_n) {}
    } edge[MAXM << 1];
    int head[MAXN << 1], cnt;
    void init() { memset(head, -1, sizeof head); cnt = 0; }
    void add_edge(int u, int v) { edge[cnt] = Edge(v, head[u]); head[u] = cnt++; }
    void insert(int u, int v) { add_edge(u, v); add_edge(v, u); }
};

void Tarjan(int, int);
void dfs(int, int);

int N, M, dfn[MAXN], low[MAXN], idx, tot;
int stk[MAXN], top;
int size[MAXN << 1], w[MAXN << 1], root[MAXN << 1];
bool vis[MAXN << 1];
LL ans;
Graph G, T;

int main() {
    scanf("%d%d", &N, &M);
    tot = N;
    G.init(); T.init();
    for (int i = 0; i < M; ++i) {
        int u, v; scanf("%d%d", &u, &v);
        G.insert(u, v);
    }
    for (int i = 1; i <= N; ++i) {
        w[i] = -1, size[i] = 1;
        if (!dfn[i]) Tarjan(i, 0);
    }
    for (int i = 1; i <= tot; ++i)
        if (!vis[i]) {
            root[i] = i;
            dfs(i, 0);
        }
    for (int i = 1; i <= tot; ++i) ans += (LL)(size[root[i]] - size[i]) * size[i] * w[i];
    printf("%lld\n", ans << 1);

    return 0;
}
void Tarjan(int u, int fa) {
    dfn[u] = low[u] = ++idx;
    for (int i = G.head[u]; ~i; i = G.edge[i].next) {
        int v = G.edge[i].v;
        if (v == fa) continue;
        if (!dfn[v]) {
            stk[top++] = v;
            Tarjan(v, u);
            low[u] = std::min(low[u], low[v]);
            if (low[v] >= dfn[u]) {
                int p; ++tot;
                do {
                    p = stk[--top];
                    T.insert(tot, p);
                    ++w[tot];
                } while(p ^ v);
                T.insert(tot, u);
                ++w[tot];
            }
        } else low[u] = std::min(low[u], dfn[v]);
    }
}
void dfs(int u, int fa) {
    vis[u] = 1;
    for (int i = T.head[u]; ~i; i = T.edge[i].next) {
        int v = T.edge[i].v;
        if (v == fa) continue;
        root[v] = root[u], dfs(v, u);
        ans += (LL)size[v] * size[u] * w[u];
        size[u] += size[v];
    }
}
//Rhein_E

猜你喜欢

转载自www.cnblogs.com/Rhein-E/p/10613321.html
今日推荐