题解:P6628 [省选联考 2020 B 卷] 丁香之路

题目链接

同学讲课的题,非常神秘。

解法

整个过程就是可以走重边的欧拉路径。我们可以先建出丁香路,问题就是求一条 \(s \rightarrow t\) 的欧拉路径,为了方便,我们可以建一条 \(s-t\) 的边,然后就是让图变成一个欧拉图,即将所有顶点度数变为偶数,且图联通。

首先考虑将所有点度数变为偶数,这个可以将顶点为奇数的点两两连边。这样可以保证此阶段边权最小。下一步是使图联通,所以 \(s - t\) 的边可以表示成 \(s-s+1-s+2-\ldots-t\)(假设 \(s<t\)),然后使图联通直接跑最小生成树即可,但因不能改变点度数的奇偶,所以每条边要连两次。

代码
#include <bits/stdc++.h>

#define eb emplace_back

using namespace std;

char *p1, *p2, buf[1 << 20];
#define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) ? EOF : *p1++)

template <typename T> void read(T &x) {
	x = 0; T f = 1; char c = gc();
	while(c < '0' || '9' < c) f = (c == '-') ? -1 : 1, c = gc();
	while('0' <= c && c <= '9') x = x * 10 + (c & 15), c = gc();
	x *= f;
}

template <typename T> void write(T x) {
	if(x < 0) putchar('-'), x = -x;
	static short st[20], top(0);
	do st[++top] = x % 10; while(x /= 10);
	while(top) putchar(st[top--] | 48);
	putchar(' ');
}

const int maxn = 2505, maxm = 4e6 + 5;

int n, m, s, p[maxn];
bool flag[maxn], tag[maxn];

struct Edge {
    int u, v, w;

    bool operator< (const Edge &t) const {
        return w < t.w;
    }
};

struct DSU {
    int fa[maxn];

    void init() {
        for(int i = 1; i <= n; i++) fa[i] = i;
    }

    int find(int x) {
        if(fa[x] == x) return x;
        return fa[x] = find(fa[x]);
    }

    void merge(int x, int y) {
        int u = find(x), v = find(y);
        if(u != v) fa[u] = v;
    }
} base, dsu;

int calc(int t) {
    int res = 0, tot = 0;
    for(int i = 1; i <= n; i++) if(tag[i]) p[++tot] = i;
    for(int i = 1; i <= tot; i += 2) {
        res += p[i + 1] - p[i];
        for(int j = p[i]; j < p[i + 1]; j++) dsu.merge(j, j + 1);
    }
    int last = 0;
    vector<Edge> e;
    for(int i = 1; i <= n; i++) {
        if(flag[i] || i == s || i == t) {
            if(last && dsu.find(last) != dsu.find(i)) e.eb((Edge){last, i, i - last});
            last = i;
        }
    }
    sort(e.begin(), e.end());
    for(Edge i : e) {
        int u = dsu.find(i.u), v = dsu.find(i.v);
        if(u != v) dsu.fa[u] = v, res += i.w * 2;
    }
    return res;
}

int main() {
    read(n), read(m), read(s);
    for(int i = 1; i <= n; i++) base.fa[i] = i;
    int u, v, sum = 0;
    for(int i = 1; i <= m; i++) {
        read(u), read(v);
        flag[u] = flag[v] = 1;
        tag[u] ^= 1, tag[v] ^= 1;
        sum += abs(u - v);
        base.merge(u, v);
    }
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) dsu.fa[j] = base.fa[j];
        tag[s] ^= 1, tag[i] ^= 1;
        write(sum + calc(i));
        tag[s] ^= 1, tag[i] ^= 1;
    }
    return 0;
}

每一步正确性都比较显然,但合在一起就十分神秘了。

posted @ 2026-09-29 21:33  PrinceEvan  阅读(14)  评论(0)    收藏  举报