The 2021 ICPC Asia East Continent Final Contest M 个人题解
答案具有单调性,考虑二分答案,设当且要验证的答案是 \(T\)。
先考虑最优情况下怎么安排蚂蚁进洞和出洞。我们从 \(\frac{T}{2}\) 截成两部分。
第一个观察是对称性,即可以把每个蚂蚁的进洞和出洞时刻翻转。
考虑只有一个洞的特例,我们发现是先让所有蚂蚁出洞,再进洞,这已经是其中一个最优方案。这启发我们尝试证明存在时间截断点 \(\frac{T}{2}\),使得前一半进洞和后一半出洞。
先假设只有一只蚂蚁不符合这个安排,因为前一半里面少了两个向后半的匹配,所以这只蚂蚁一定可以等待到超过 \(\frac{T}{2}\) 而不超过 \(T\) 时刻进那个洞。
再假设有 \(k\) 只不符合,这时上下会各产生 \(k\) 个空缺,我们一定存在方案让一只蚂蚁等待或者提前出洞,归纳到子问题。
有了这个限制,问题变为了二分图最大匹配,左右部的点都形成了若干区间。
如果 \(T\) 是偶数,上述判定方法是没有问题的。否则会在中间时刻产生问题,因为此时可以同时存在进洞和出洞。
我们可以证明,在中间时刻产生的进洞和出洞数量相等。同时因为洞就那么多,所以两者都不超过 \(\frac{n}{2}\)。
这个证明我们可以假想把中间时刻拆解成一段连续的时刻,套用上面的证明。
于是我们可以设置为原图中每个点的流量为 2, 中间时刻的点流量为 1,做最大匹配即可。
朴素的实现复杂度为 \(O(n\log n\log V)\),观察发现是类似区间平移的过程,可以做到 \(O(n(\log n+\log V))\)。
下面的代码是朴素实现。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double dou;
typedef pair<int,int> pii;
#define fi first
#define se second
#define mapa make_pair
typedef long double ld;
typedef unsigned long long ull;
#define ep emplace_back
template <typename T>inline void read(T &x){
x=0;char c=getchar();bool f=0;
for(;c<'0'||c>'9';c=getchar()) f|=(c=='-');
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
x=(f?-x:x);
}
const int N=1e6+5;
int T, n; ll m;
ll a[N];
struct node{
ll x; int tp, cnt;
inline bool operator <(const node &t)const{
return x<t.x;
}
};
bool check(ll lim){
ll h=lim/2;
vector<node> vec;
for(int i=1; i<=n; ++i){
vec.push_back((node){a[i]+1, 0, 2});
vec.push_back((node){a[i]+h+1, 0, -1});
vec.push_back((node){a[i]+lim-h+1, 0, -1});
vec.push_back((node){h-a[i], 1, 1});
vec.push_back((node){lim-h-a[i], 1, 1});
vec.push_back((node){lim-a[i], 1, -2});
}
sort(vec.begin(), vec.end());
__int128 sum=0, lst=0, lsum=0, rsum=0;
for(int i=0; i+1<(int)vec.size(); ++i){
if(vec[i].tp==0){
lsum+=vec[i].cnt;
}
else{
rsum+=vec[i].cnt;
}
if(vec[i].x==vec[i+1].x) continue;
if(lsum>=rsum){
sum+=rsum*(vec[i+1].x-vec[i].x);
lst+=(lsum-rsum)*(vec[i+1].x-vec[i].x);
}
else{
lst+=lsum*(vec[i+1].x-vec[i].x);
__int128 cur=min(rsum*(vec[i+1].x-vec[i].x), lst);
sum+=cur; lst-=cur;
}
if(sum>=m) return true;
}
return false;
}
void solve(){
read(n); read(m); m*=2;
for(int i=1; i<=n; ++i) read(a[i]);
ll L=1, R=1e15, mid, ret=0;
while(L<=R){
mid=(L+R)/2;
if(check(mid)){
ret=mid; R=mid-1;
}
else{
L=mid+1;
}
}
printf("%lld\n", ret);
}
int main(){
// freopen("D:\\nya\\acm\\A\\test.in","r",stdin);
// freopen("D:\\nya\\acm\\A\\test.out","w",stdout);
read(T);
while(T--){
solve();
}
return 0;
}