题解:P6898 [ICPC 2014 WF] Metal Processing Plant
一个需要观察优化的小 trick。
题意:给出一个完全图和每条边边权,要求把所有点划分成两个点集 \(A,B\),定义 \(D(A)=\max_{x,y\in A}d(x,y)\),求 \(D(A)+D(B)\) 最小值。\(n\le 200\)。
做法:
直接枚举 \(D(A)\) 再二分,用 2-sat 判定可以做到 \(O(n^4\log n)\)。用双指针枚举 \(D(B)\) 可以做到 \(O(n^4)\)。
考虑怎么优化,先不妨假设 \(D(A)\ge D(B)\)。考虑 \(D(A)\) 变小的时候会有什么变化,注意到 \(>D(A)\) 在 2-sat 的边是双向的,我们不妨直接用并查集缩起来。考虑如果出现了一个奇环,那么在之后就直接爆掉了,所以我们对这条边计算答案之后,后面的就没有必要再计算了,因为一定不合法;如果出现了偶环,那么我们需要这个端点是同色的,但是中间就会出现同色,这样这种情况就不算入答案。而如果不出现环就跑,这样只需要对 \(O(n)\) 个 \(D(A)\) 处理。注意别用双指针要不然退化成 \(O(n^4)\) 了,总复杂度 \(O(n^3\log n)\)。
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 205 + 5;
int n, d[maxn][maxn], pre[maxn * 2];
struct Edge {
int x, y, w;
friend bool operator<(Edge x, Edge y) {
return (x.w != y.w ? x.w < y.w : x.x < y.x);
}
};
vector<Edge> ed;
void prepare() {
for (int i = 1; i <= 2 * n; i++)
pre[i] = i;
}
int fnd(int x) {
return (pre[x] == x ? x : pre[x] = fnd(pre[x]));
}
int unn(int x, int y) {
if(fnd(x) == fnd(y + n))
return 0;
pre[fnd(x)] = fnd(y + n);
pre[fnd(y)] = fnd(x + n);
if(fnd(x) == fnd(x + n))
return -1;
return 1;
}
int ans = 9e18;
vector<int> e[maxn * 2];
int dfn[maxn * 2], low[maxn * 2], st[maxn * 2], top, tot, inst[maxn * 2], pos[maxn * 2], cnt;
void tarjan(int u) {
dfn[u] = low[u] = ++tot, st[++top] = u; inst[u] = 1;
for (int i = 0; i < e[u].size(); i++) {
int v = e[u][i];
if(!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(inst[v])
low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u]) {
cnt++;
while(st[top + 1] != u) {
int p = st[top--];
pos[p] = cnt, inst[p] = 0;
}
}
}
bool chk(int x, int y) {
for (int i = 1; i <= 2 * n; i++)
e[i].clear(), dfn[i] = low[i] = 0, pos[i] = 0;
top = tot = cnt = 0;
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
if(d[i][j] > x)
e[i].push_back(j + n),
e[j].push_back(i + n);
if(d[i][j] > y)
e[i + n].push_back(j),
e[j + n].push_back(i);
}
}
for (int i = 1; i <= 2 * n; i++)
if(!dfn[i]) {
for (int j = 1; j <= 2 * n; j++)
st[j] = 0;
tarjan(i);
}
for (int i = 1; i <= n; i++)
if(pos[i] == pos[i + n])
return 0;
return 1;
}
signed main() {
// freopen("test.in", "r", stdin);
// freopen("baoli.out", "w", stdout);
cin >> n;
if(n <= 2) {
cout << 0 << endl;
return 0;
}
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++)
cin >> d[i][j], d[j][i] = d[i][j],
ed.push_back(Edge{i, j, d[i][j]});
ed.push_back(Edge{0, 0, 0});
sort(ed.begin(), ed.end());
prepare();
for (int i = ed.size() - 1; i >= 0; i--) {
if(i != ed.size() - 1) {
int f = unn(ed[i + 1].x, ed[i + 1].y);
if(f == 0)
continue;
if(f == -1) {
i++;
int l = -1, r = ed[i].w;
// cout << "adf" << endl;
while(l + 1 < r) {
int mid = l + r >> 1;
if(chk(ed[i].w, mid))
r = mid;
else
l = mid;
}
// cout << ed[i].w << " " << r << " " << i << endl;
if(chk(ed[i].w, r)) {
ans = min(ans, ed[i].w + r);
// if(n == 200)
// cout << ed[i].w << " " << r << endl;
}
break;
}
}
int l = -1, r = ed[i].w;
// cout << "adf" << endl;
while(l + 1 < r) {
int mid = l + r >> 1;
if(chk(ed[i].w, mid))
r = mid;
else
l = mid;
}
// cout << ed[i].w << " " << r << " " << i << endl;
if(chk(ed[i].w, r)) {
ans = min(ans, ed[i].w + r);
// if(n == 200)
// cout << ed[i].w << " " << r << endl;
}
i++;
if(i != ed.size()) {
l = -1, r = ed[i].w;
// cout << "adf" << endl;
while(l + 1 < r) {
int mid = l + r >> 1;
if(chk(ed[i].w, mid))
r = mid;
else
l = mid;
}
// cout << ed[i].w << " " << r << " " << i << endl;
if(chk(ed[i].w, r)) {
ans = min(ans, ed[i].w + r);
// if(n == 200)
// cout << ed[i].w << " " << r << endl;
}
}
i--;
}
cout << ans << endl;
return 0;
}
/*
*/

浙公网安备 33010602011771号