2019 ICPC 沈阳区域赛-L Flowers(二分答案)

Recently Jack becomes much more romantic. He would like to prepare several bunches of flowers.Each bunch of flowers must have exactly M flowers. As Jack does not want to be boring, he hopes that flowers in the same bunch are all different species. Now there are N species of flowers in the flower shop, and the number of the i-th species of flower is a​i. Now Jack would like to know how many bunches of flowers he can prepare at most.(Flowers are used to propose.)

Input
The first line contains an integer T (1≤T≤10) — the number of test cases.In the first line of each test case, there are two integers N, M (1≤N,M≤300000) — the number of flowers’ species and the number of flowers in a bunch.In the second line of each test case, there are N integers — the i-th integer indicates a​i
(1≤a​i≤109​), the number of i-th species’ flowers.

Output
For each test case, output one integer in one line — the answer of the corresponding test case.

Sample Input
1
5 3
1 1 1 2 1
Sample Output
2

1.题目大意:给出n种花,现在每一种都有ai个,要扎成花束,该花束满足有m多花并且每种花只能有1朵,问最多能扎到几束花

2.比赛时没能写出来,上一次写二分答案是在2019暑假,如今忘的差不多了,主要是一直在学习新的东西,没时间对每个知识点刷题巩固,导致以前学过的不太牢,但也没办法。请教了学长,但是知道怎么做之后,花了我三天去改错,之前求前缀和一直用的树状数组,但是不知道为什么一直WA,检查了n遍没发现错误。今天实在没办法了,写了数组保存前缀和,然后就过了

3.言归正传,正解:先假设最多扎x束花,因为每种花在每一束最多有一朵,那么需要每一种花的数量为bi = min { ai , x },由于每种花的数量可能大于x也可能小于x,那么即使数量多于x也再扎不了一束了,因为假设的是当前最大的x。如果x能满足x*m<=Σbi,那么x就可以作为答案。因为我们要二分答案,因此先设定一个范围,由于范围没有那么大,那么我们设l=0,r=sum(实际上更严谨的是l=n/m,r=sum/m),对区间二分答案即可。可以证明,如果当前x满足条件后,那么右边可能仍存在一个更大的x满足条件,因此满足条件左边界右移,否则右边界左移

4.二分答案有很多写法,有时候数据范围很大时,直接二分一百次,一定能找到答案,但是本题一般的二分即可

写法一:

while(l<=r){
	mid=(l+r)>>1;
	if(check(mid)){
		ans=mid;
		l=mid+1;
	}else r=mid-1;
}

写法二:

while(l<r){
	mid=(l+r)>>1;
	if(check(mid+1)){
		l=mid+1;
	}else r=mid;
}
ans=l;

写法三:

for(int i=1;i<=100;i++){
	mid=(l+r)>>1;
	if(check(mid)){
		ans=mid;
		l=mid+1;
	else r=mid-1;
}

代码:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
#define lowbit(x) (x&(-x))
const int N=3e5+10;
ll a[N],d[N];
int t,n,m;
ll ans;

bool check(ll x){
    ll res;
    int r=upper_bound(a+1,a+1+n,x)-a;   //找大于x的第一个元素
    if(r!=0) res=(d[r-1]+(n-r+1)*x)/m;  //当数组所有元素都小于x时,返回的是0,要特判
    else res=n*x/m;
    if(x<=res) return true;
    else return false;
}

void solve(ll sum){
    ll l=n/m,r=sum/m,mid;
    ans=l;
    /*while(l<=r){
        mid=(l+r)>>1;
        if(check(mid)){
            ans=mid;
            l=mid+1;
        }else r=mid-1;
    }*/
    for(int i=1;i<=100;i++){
        mid=(l+r)>>1;
        if(check(mid)){
            ans=mid;
            l=mid+1;
        }else r=mid-1;
    }
}

int main(){
    scanf("%d",&t);
    while(t--){
        ll res=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            res+=a[i];
        }
        sort(a+1,a+1+n);
        for(int i=1;i<=n;i++){
            d[i]=d[i-1]+a[i];
        }
        solve(res);
        printf("%lld\n",ans);
    }
    return 0;
}
发布了128 篇原创文章 · 获赞 7 · 访问量 5248

猜你喜欢

转载自blog.csdn.net/qq_44691917/article/details/104768205
今日推荐