题解: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;
}
每一步正确性都比较显然,但合在一起就十分神秘了。

浙公网安备 33010602011771号