题解:P10046 [CCPC 2023 北京市赛] 哈密顿
masterhuang · · 题解
由于懒惰进行了一个无耻的抄,希望各位谅解
法 1 (良心WA题人)
拆绝对值套路。考虑将相邻两条边的绝对值拆出来,有以下四种:
-
a_i - b_j \to a_j - b_k: a_j - b_j -
a_i - b_j \to b_k - a_j: -a_j - b_j -
b_j - a_i \to b_k - a_j: b_j - a_j -
即对每种情况,考虑对于中间点
显然合法图都能被这四种拼出来。考虑怎么拼才是合法:
显然由于正负个数相等,第二种和第四种个数要相等。
而第一种和第三种分别可以单独出现。若要同时出现,则至少有一个第二种和第四种(过渡)。
特判全一和全三。
显然可以从一开始全都是第一种或第三种(取最优),然后不断加入第二种和第四种(进行调整)边取最大值。可以用优先队列简单维护。
具体的:每次贪心的,同时加入当前最优的调整
若
优先队列调整,复杂度
关键代码参考:
for(int i=1;i<=n;i++)q1.emplace(a[i].fi+a[i].se-abs(a[i].fi-a[i].se),i),q2.emplace(-a[i].fi-a[i].se-abs(a[i].fi-a[i].se),i);
while(q1.size()&&q2.size()){
if(q1.top().se!=q2.top().se){
res+=q1.top().first+q2.top().first;
}else{
int A=q1.top().first,B=q2.top().first;
q1.pop();q2.pop();
if(!q1.size())break;
res+=max(A+q2.top().first,B+q1.top().first);
}
q1.pop();q2.pop();
ans=max(ans,res);
}
法 2 (TachiBK)
这个代码是最短的。
有个贪心:我们贪心强制对
此时只有少部分情况不合法,我们参考法
参考法
否则一定一三均有,且无二四。考虑调整一组最优的符号即可,就把排序完的第
如果第
时间复杂度为除排序外线性。
法 3 (云斗题解+Alex_Wei)
不是很建议,有点小复杂
首先把答案设为
考虑钦定一些边
于是只需要找两个集合
将
先这么选着,设两边选的对应的
对
- 若
\Delta |A| = -1 ,则- 若
c_k \neq d_{n-k+1} 或者|A| = 1 ,则将c_k 和d_{n-k+1} 分别从A 和B 中删去。 - 否则删去
c_k 和d_{n-k+2} ,或c_{k-1} 和d_{n-k+1} 。
- 若
- 若
\Delta |A| = 0 ,则将c_k 和d_{n-k+1} 删去,换成c_k 和d_{n-k} ,或c_{k+1} 和d_{n-k+1} 。 - 若
\Delta |A| = 1 ,则- 若
c_{k+1} \neq d_{n-k} 或|A| = n-1 ,则加入c_{k+1} 和d_{n-k} 。 - 否则加入
c_{k+1} 和d_{n-k-1} ,或c_{k+2} 和d_{n-k} 。
- 若
正确性证明分两步,一步是证明给出的方案是在固定
时间复杂度为除排序外线性。
// code by Alex_Wei
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
using pdi = pair<double, int>;
using pdd = pair<double, double>;
using ull = unsigned long long;
using LL = __int128_t;
#define ppc(x) __builtin_popcount(x)
#define clz(x) __builtin_clz(x)
bool Mbe;
// ---------- templates above ----------
constexpr int N = 1e5 + 5;
int n;
struct dat {
int val, id;
bool operator < (const dat &z) const {
return val < z.val;
}
} a[N], b[N];
void solve() {
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i].val >> b[i].val;
a[i].id = b[i].id = i;
}
sort(a + 1, a + n + 1);
sort(b + 1, b + n + 1);
int l = 0, r = n + 1;
map<int, int> mp;
ll ans = 0;
while(l < n) {
if(a[l + 1].val > b[r - 1].val) break;
mp[a[++l].id]++, mp[b[--r].id]--;
ans += b[r].val - a[l].val;
}
bool ok = 0;
for(pii it : mp) ok |= it.second > 0;
if(!ok && l < n && l) {
ll res = -1e18;
auto f = [&](int l, int r) {
return ll(b[r].val - a[l].val);
};
if(a[l].id != b[r].id || l == 1) res = max(res, -f(l, r));
else res = max(res, max(-f(l, r + 1), -f(l - 1, r)));
res = max(res, max(f(l, r - 1), f(l + 1, r)) - f(l, r));
if(a[l + 1].id != b[r + 1].id || l == n - 1) res = max(res, f(l + 1, r - 1));
else res = max(res, max(f(l + 2, r - 1), f(l + 1, r - 2)));
ans += res;
}
ans *= 2;
for(int i = 1; i <= n; i++) ans += a[i].val - b[i].val;
cout << ans << endl;
}
bool Med;
signed main() {
fprintf(stderr, "%.3lf MB\n", (&Mbe - &Med) / 1048576.0);
// ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
#ifdef ALEX_WEI
FILE* IN = freopen("1.in", "r", stdin);
FILE* OUT = freopen("1.out", "w", stdout);
#endif
int T = 1;
while(T--) solve();
fprintf(stderr, "%.3lf ms\n", 1e3 * clock() / CLOCKS_PER_SEC);
return 0;
}
/*
g++ a.cpp -o a -std=c++14 -O2 -DALEX_WEI
*/