题解:AT_abc444_f [ABC444F] Half and Median
kaikatandy · · 题解
简单题,给出一种大常数,没有脑子的解法。
考虑到将问题转化成“中位数
注意到一个数不断进行对半分,最后只会有
那么就可以在
对于
从现在开始,一旦出现
::::info[贪心]
在以下过程中对
我们考虑将
先考虑分解
然后从大到小,将
我们使用 map 维护可重集,可以做到
::::success[代码,最大点 3975 ms]
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 7;
int n, m, a[N];
map<int, int> cal(int x, int lim){
map<int, int> cur, nxt;
cur[x] = 1;
while(1){
nxt.clear();
for(auto ss: cur){
int fs, sn;
tie(fs, sn) = ss;
int A = fs / 2, B = fs - A;
if(A >= lim && B >= lim) nxt[A] += sn, nxt[B] += sn;
else nxt[fs] += sn;
}
if(cur == nxt) break;
cur = nxt;
}
return cur;
}
int CEIL(int x, int y){
return x / y + (x % y > 0);
}
bool check(int med){
int len = n + m, req = (len + 1) / 2;
int cntl = 0;
for(int i = 1; i <= n; i ++) cntl += (a[i] < med);
if(cntl >= req) return false;
int sum = 0, cnt = 0;
map<int, int> res;
for(int i = 1; i <= n; i ++){
if(a[i] < med) sum += a[i];
else{
map<int, int> tmp = cal(a[i], med);
for(auto ss: tmp) res[ss.first] += ss.second, cnt += ss.second;
}
}
if(cnt < req) return false;
if(cnt + sum >= len) return true;
if(res.count(med * 2 - 1)){
int cc = res[med * 2 - 1];
sum += cc * (med - 1);
res.erase(med * 2 - 1);
res[med] += cc;
}
if(cnt + sum >= len) return true;
for(auto it = res.rbegin(); it != res.rend(); it ++){
int num, app;
tie(num, app) = (*it);
if(num == 1) break;
int trs = min(app, CEIL((len - cnt - sum), (num - 1)));
cnt -= trs, sum += trs * num;
if(cnt < req) return false;
if(cnt + sum >= len) return true;
}
if(cnt < req) return false;
if(cnt + sum >= len) return true;
return false;
}
void solve(){
cin >> n >> m;
for(int i = 1; i <= n; i ++) cin >> a[i];
int l = 0, r = *max_element(a + 1, a + n + 1) + 1;
while(l + 1 < r){
int mid = (l + r) >> 1;
if(check(mid)) l = mid;
else r = mid;
}
cout << l << "\n";
}
signed main(){
ios::sync_with_stdio(0), cin.tie(0);
int t; cin >> t;
while(t --) solve();
return 0;
}
::::