刷点题~
1.二分+多路归并算法
对于每一个技能,我们把它看成一个等差数列,我们把所有可能都放到一个集合里,排个序,取前m个大即可,现在考虑优化,假如m不是很大,我们直接用优先队列即可,但是这里m很大,于是我们考虑二分,我们二分一下第m位选什么-x,那么大于X的>=m个,大于x的<m个,这里x就有二分性质,当我们确定x,那么对于每一个等差数列,我们可以用O(1)求出来,因此复杂度为nlogn。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100010;
int n,m;
int a[N],b[N];
bool check(int mid){
LL res=0;
for(int i=0;i<n;i++){
if(a[i]>=mid) res+=(a[i]-mid)/b[i]+1;
}
return res>=m;
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++) scanf("%d%d",&a[i],&b[i]);
int l=0,r=1e6;
while(l<r){
int mid=(l+r+1)/2;
if(check(mid)) l=mid;
else r=mid-1;
}
LL res=0;
LL cnt=0;
for(int i=0;i<n;i++){
if(a[i]>=r){
int c=(a[i]-r)/b[i]+1;
int end=a[i]-(c-1)*b[i];
cnt+=c;//cnt计算了重复的,当它>m时减去多的R
res+=(LL)(a[i]+end)*c/2;
}
}
cout<<res-(cnt-m)*r;
}
2.
对于每一个数据,我们求出来满足[A/V](下取整)=B的v的范围,然后取交集即可。
我们考虑一下A/V与V的函数图像:
因此就可以二分了,vmin=A/V<=B的最小的V,vmax=A/V<=B-1的最小的V-1(当然不用二分用公式也可)
下面是公式:
A/V在【B,B+1)内,转一下可得V为【A/B,A/(B+1))即可。
3.前缀和
我们看最终情况,它是中间一段是画的,旁边两端是被摧毁的,画的长度是n/2的上取整,事实上,我们可以取到任意的该长度的区间,如何证明?
首先,我们任取该长度的区间,我们大致做一下对称:
先看看奇数长度,对于第一步,我们先话中间空余的,以后哪一端是要坏的我们选那一段,这样子就可以了。
对于偶数长度,第一步任取接下来跟奇数一样即可。
因此问题就是求某一区间的最大和,这个用前缀和即可