[APIO2018] Duathlon 铁人两项 圆方树,DP

[APIO2018] Duathlon 铁人两项

LG传送门

圆方树+简单DP。

不会圆方树的话可以看看我的另一篇文章

考虑暴力怎么写,枚举两个点,答案加上两个点之间的点的个数。

看到题面中的一句话:

考虑到安全因素,选择的路径经过同一个点至多一次。

换句话说就是简单路径,用(广义)圆方树的基本条件已经满足,可以把问题变到树上:令方点的权值为所在点双的大小,圆点会被算重所以点权为\(-1\),统计任意两点间的点权和就好了。直接做是\(n ^ 2\)的,考虑枚举每一个点看会产生多少贡献,对答案的贡献就乘个点权。

注意图不一定联通。

#include <cstdio>
#include <cctype>
#include <vector>
#define R register
#define I inline
#define B 1000000
#define L long long
using namespace std;
const int N = 200003;
char buf[B], *p1, *p2;
I char gc() { return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, B, stdin), p1 == p2) ? EOF : *p1++; }
I int rd() {
    R int f = 0;
    R char c = gc();
    while (c < 48 || c > 57)
        c = gc();
    while (c > 47 && c < 58)
        f = f * 10 + (c ^ 48), c = gc();
    return f;
}
L ans = 0;
int s[N], w[N], dfn[N], low[N], vis[N], sta[N], siz[N], n, tim, cnt, stp, tot;
vector <int> g[N], G[N];
I int min(int x, int y) { return x < y ? x : y; }
void dfs(int x) {
    dfn[x] = low[x] = ++tim, sta[++stp] = x, w[x] = -1, ++tot;
    for (R int i = 0, y, z; i < s[x]; ++i)
        if (!dfn[y = g[x][i]]){
            dfs(y), low[x] = min(low[x], low[y]);
            if (low[y] >= dfn[x]) {
                w[++cnt] = 1, G[x].push_back(cnt), G[cnt].push_back(x);
                do {
                    z = sta[stp--], ++w[cnt], G[z].push_back(cnt), G[cnt].push_back(z);
                } while (z ^ y);
            }
        }
        else
            low[x] = min(low[x], dfn[y]);
}
void calc(int x, int f) {
    siz[x] = vis[x] = x <= n;
    L tmp = 0;
    for (R int i = 0, y, S = G[x].size(); i < S; ++i)
        if ((y = G[x][i]) ^ f)
            calc(y, x), tmp += 2ll * siz[y] * siz[x], siz[x] += siz[y];
    tmp += 2ll * siz[x] * (tot - siz[x]), ans += tmp * w[x];
}
int main() {
    freopen("a.in", "r", stdin);
    R int m, i, x, y;
    cnt = n = rd(), m = rd();
    for (i = 1; i <= m; ++i)
        x = rd(), y = rd(), g[x].push_back(y), g[y].push_back(x);
    for (i = 1; i <= n; ++i)
        s[i] = g[i].size();
    for (i = 1; i <= n; ++i)
        if (!vis[i])
            stp = 0, tot = 0, dfs(i), calc(i, 0);
    printf("%lld", ans);
    return 0;
}

posted @ 2019-01-19 10:52  newbiechd  阅读(191)  评论(0编辑  收藏  举报