题解 [UOJ #177] 新年的腮雷
- 在 c++20 中传入 STL 的比较因子记得写成
struct cmp{inline bool operator () (...) const {}};
,需要加这个const
否则可能 CE
发现完全不可贪心,也不太能 DP
所以考虑二分答案转化为判定
然而判定性 DP 也不太能写
所以考虑化为拆分
于是问题变为将一个 \(mid\) 按逆过程拆分成 \(n\) 个数 \(\{c_1,\cdots,c_n\}\),问是否能让 \(\forall i, c_i\geqslant a_i\)
于是因为 \(b\) 数组是相同的所以每次肯定拆最大能拆的
令当前拆出来的可重集为 \(s\),剩的 \(a\) 组成集合 \(t\)
如果当前最大的 \(t<\max\{s_i\}-b_1\),就把这个 \(t\) 和一个能匹配的最小的 \(s_i\) 匹配了就好了
复杂度 \(O(n\log^2n)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m;
multiset<ll> s, t;
ll a[N], b[N], lim;
bool check(ll mid) {
// cout<<"check: "<<mid<<endl;
s.clear(); t.clear(); s.insert(mid);
for (int i=1; i<=n; ++i) t.insert(a[i]);
while (s.size()&&t.size()&&s.size()!=t.size()) {
// cout<<"s: "; for (auto it:s) cout<<it<<' '; cout<<endl;
// cout<<"t: "; for (auto it:t) cout<<it<<' '; cout<<endl;
auto it1=*s.rbegin(), it2=*t.rbegin();
if (it1-b[1]>=it2) {
s.erase(s.find(it1));
for (int j=1; j<=m; ++j) s.insert(it1-b[j]);
}
else {
auto it=s.lower_bound(it2);
if (it==s.end()) return 0;
else s.erase(it);
t.erase(t.find(it2));
}
}
if (s.size()!=t.size()) return 0;
for (auto it1=s.begin(),it2=t.begin(); it1!=s.end(); ++it1,++it2)
if (*it1<*it2) return 0;
return 1;
}
signed main()
{
n=read(); m=read();
for (int i=1; i<=n; ++i) lim=max(lim, a[i]=read());
for (int i=1; i<=m; ++i) b[i]=read();
sort(b+1, b+m+1);
// cout<<"b: "; for (int i=1; i<=m; ++i) cout<<b[i]<<' '; cout<<endl;
ll l=1, r=lim+(n-1)/(m-1)*b[m], mid;
while (l<=r) {
mid=(l+r)>>1;
if (check(mid)) r=mid-1;
else l=mid+1;
}
printf("%lld\n", r+1);
return 0;
}