正题
题目链接:https://www.luogu.com.cn/problem/AT4437
题目大意
有nnn个点的一张有向完全图,每个点有两个点权a,ba,ba,b。连接x,yx,yx,y两个点的边权为min{ax,by}min\{a_x,b_y\}min{ax,by},求一条权值和最小的哈密顿回路。
1≤n≤105,1≤a,b≤1091\leq n\leq 10^5,1\leq a,b\leq 10^91≤n≤105,1≤a,b≤109
解题思路
又是minminmin又是权值最小,我们可以把问题转换为从xxx走到yyy的权值可以选择axa_xax或者byb_yby,然后求最小的权值和。
一个暴力的想法是对于每个axa_xax对应一个byb_yby来匹配,但是这样很显然容易导致选出的是若干个小环。
我们可以考虑具体一个点的贡献,我们根据aaa和bbb是否产生了贡献记为一个二进制位,那么每个点的贡献有00,01,10,1100,01,10,1100,01,10,11,考虑什么时候一个方案合法。
把每个a/ba/ba/b视为一个二分图,并且aia_iai向bib_ibi连边,然后我们之后连的边中要求两个端点恰好有一个111,确立如下图所示规则:
- 11+00=10/01/ring11+00=10/01/ring11+00=10/01/ring
- 01+01=01/ring01+01=01/ring01+01=01/ring,同理有10+10=10/ring10+10=10/ring10+10=10/ring
- 00+01/10=0000+01/10=0000+01/10=00
- 11+01/10=1111+01/10=1111+01/10=11
不难发现一个合法的构造只有两种情况
- 全部都是010101或者101010
- 000000和111111各有k(k≥1)k(k\geq 1)k(k≥1)个,其余01/1001/1001/10任意
第一种情况直接计算
第二种情况我们对于每一个默认为01/1001/1001/10中权值最小的一个,然后一个01/10→11(ans+max{ai,bi})01/10\rightarrow 11(ans+max\{a_i,b_i\})01/10→11(ans+max{ai,bi}),一个01/10→00(ans−min{ai,bi})01/10\rightarrow 00(ans-min\{a_i,b_i\})01/10→00(ans−min{ai,bi}),我们可以用两个堆分别维护max{ai,bi}max\{a_i,b_i\}max{ai,bi}和min{ai,bi}min\{a_i,b_i\}min{ai,bi}
需要注意的是由于第二种情况至少需要一个00/1100/1100/11所以就算第一次会让答案变大也得变,而且有可能出现第一次选择的max{ai,bi}max\{a_i,b_i\}max{ai,bi}和min{ai,bi}min\{a_i,b_i\}min{ai,bi}是同一个iii,需要特判。
时间复杂度:O(nlogn)O(n\log n)O(nlogn)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define mp(x,y) make_pair(x,y)
#define ll long long
using namespace std;
const ll N=1e5+10;
ll n,a[N],b[N],ans1,ans2,ans;
priority_queue<pair<ll,ll> > q1,q2;
signed main()
{scanf("%lld",&n);for(ll i=1;i<=n;i++){scanf("%lld%lld",&a[i],&b[i]);ans1+=a[i];ans2+=b[i];q1.push(mp(min(a[i],b[i]),i));q2.push(mp(-max(a[i],b[i]),i));ans+=min(a[i],b[i]);}pair<ll,ll> x=q1.top(),y=q2.top();if(x.second==y.second){q1.pop();q2.pop();pair<ll,ll> l=q1.top(),r=q2.top();ans+=min(-r.first-x.first,-y.first-l.first);}else{q1.pop();q2.pop();ans+=-y.first-x.first;while(1){ll x=q1.top().first,y=-q2.top().first;q1.pop();q2.pop();if(x<=y)break;ans+=y-x;}}printf("%lld\n",min(ans,min(ans1,ans2)));return 0;
}