题面
题解
早上写了一篇,就还想再写一篇
挺不错的一个题
先来讲讲部分分吧
第一档13分的很简单,直接爆搜就可以了
第二档11分的也很简单,直接排个序就可以了
然后有一档6分的,
m
=
2
,
b
=
{
1
,
1
}
m=2,b=\{1,1\}
m=2,b={1,1},这的话,稍作思考,可以发现,每一次一定是选两个最小的数合起来
然后剩下的档我就不会了。。
算了一下只可以拿到
13
+
11
+
6
=
30
13+11+6=30
13+11+6=30
蛮低的暴力分吧
其实如果你会第三档,
24
24
24分的,基本上离正解也不远了
先说
24
24
24分的吧
正解要想到二分答案,然后倒过来做
那么问题就变成了,你一开始有一个数
x
x
x,然后每一次,你可以把你的数集里面的某一个数
a
a
a,把他拆成
m
m
m个数,并且要求拆出来的m个数,都要满足都比
a
i
a_i
ai要大。正确性其实挺显然的。
然后你考虑,满分的话
问题就等价于,你拆成n个数以后,可以找到一个对应方案,使得每一个数都比对应的
a
i
a_i
ai大
这个做法其实也不错的
我们可以进行讨论
我们把当前数集的值
x
x
x拿出来,然后看一下目标数集的最大值
y
y
y,再看一下
b
b
b的最小值
b
′
b'
b′
如果
x
−
b
′
<
y
x-b'<y
x−b′<y,那么显然,y是不可能由别的东西拆开得到了,因此,找一个刚好比他大的数,对应起来即可
否则就把x拆开,这个,其实正确性也挺显然的,因为你拆别的不会比拆这个要优
当你得到了n个数的时候,check一下就可以了
时间复杂度显然是对的
CODE:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<set>
using namespace std;
const int N=50005;
int n,m;
int a[N],b[N];
multiset<int> s;
multiset<int> :: iterator it;
bool check (int x)//这个答案行不行
{
s.clear();s.insert(x);
int now=n;
while (s.size()<now)//现在有多少个数了
{
if (s.empty()) return false;
it=--s.end();
if ((*it)-b[1]<a[now])//不行
{
it=s.lower_bound(a[now]);
if (it==s.end()) return false;
s.erase(it);now--;
}
else
{
for (int u=1;u<=m;u++)
s.insert((*it)-b[u]);
s.erase(it);
}
}
int i=1;
for (it=s.begin();it!=s.end()&&i<=now;it++,i++)
if (a[i]>(*it)) return false;
return true;
}
int main()
{
scanf("%d%d",&n,&m);
for (int u=1;u<=n;u++) scanf("%d",&a[u]);
for (int u=1;u<=m;u++) scanf("%d",&b[u]);
sort(b+1,b+1+m);sort(a+1,a+1+n);
int l=0,r=1000000000,ans=-1;
while (l<=r)
{
int mid=(l+r)>>1;
if (check(mid)) {r=mid-1;ans=mid;}
else l=mid+1;
}
printf("%d\n",ans);
return 0;
}