codeforces 837F. Prefix Sums 思维+二分+组合数

来源:互联网 发布:一级域名注册 编辑:程序博客网 时间:2024/06/05 14:47

题目链接


题意:

p(x)表示对于一个元素个数为m的x的序列,会产生一个序列y 为m+1个元素,且有y[i]=sum(x[j]) 0<=j<i

现在给你一个长度为n的序列A0,给你一个k,问你要至少进行几次操作,使得Ai 当中存在某一个元素的值>=k。

     Ai = p(Ai-1)


思路:


这个题目我的失败之处在于没想到二分.求最少的操作次数啊....而且满足单调性啊,假如我进行w次,那么我进行w-1次一定也是可以的啊。

好了,现在我们想到了二分,那么下面我们来考虑如何快速的求出二分的次数是否满足条件.

首先明确 A0的每个元素都是>=0的,那么也就是说新产生的序列中最后一个元素一定是所有数中最大的.那么如果最后一个满足就一定可以了.那么我们来看看能不能快速的求出新产生的序列的最后一个值.

这里由于新产生的序列第一个一定是0,越往下0越多,我们不考虑这些为0的只考虑这些非0的,保证元素个数是不变的.

先考虑一个简单的,我举个例子,长度为3的初始序列.1 0 0 和0 1 0 和0 0 1


 



然后就可找到规律了,把每一哥分离出来计算,然后二分就好.


这里有个技巧就是C(n,m) 比较m和n-m的大小 计算小的,然后过程中就满足>=k 直接返回/

#include<bits/stdc++.h>using namespace std;typedef long long ll;const int maxn = 2e5 + 10;ll n,k;ll a[maxn];bool check(ll x){    double sum = 0,tmp = 0;    for(int i = 0;i < n;i++)    {        if(a[i] == 0)            continue;        ll y = n - 1 - i;        ll mm = x - 1, nn = mm + y;        mm = min(nn - mm,mm);        tmp = a[i];        for(ll j = 1;j <= mm;j++)        {            tmp = tmp * (nn - mm + j)/j;            if(tmp >= k)                return true;        }        sum += tmp;        if(sum >= k)            return true;    }    return false;}void solve(){    ll l = 1, r = k,mid;    while(l <= r)    {        mid = (l + r) >> 1;        if(check(mid))            r = mid - 1;        else            l = mid + 1;    }    printf("%lld\n",l);    return ;}int main(){    scanf("%lld %lld",&n,&k);    int flag = 0;    for(int i = 0;i < n;i++)    {        scanf("%lld",&a[i]);        if(a[i] >= k)        flag = 1;    }    if(flag)        puts("0");    else        solve();    return 0;}



原创粉丝点击