“玲珑杯”ACM比赛 Round #13 B -- 我也不是B,倍增+二分!

                                                  B  我也不是B
  这个题做了一下午,比赛两个小时还是没做出来,比完赛才知道要用一个倍增算法确定区间,然后再二分右端点。
  题意:定义一个序列的混乱度为累加和:b[i]*v[i],b[i]为这个序列中第i小的数,v[]数组是给定的。如果当前加进来的数购车的数构成的序列的混乱度大于m,则将当前的序列扔掉,然后将变量C加一,现在给出要加进来的序列的顺序,和v[]数组,求最终C的值。

  思路:枚举左端点,二分右端点,暴力判断混乱度与M的关系,如果Me为0,只能一个一个删除,那么二分貌似会将复杂度拉高,所以为了避免这种情况我们要用倍增算法确定二分区间,假设当前左端点为i,于是枚举一个k使得[i,i+2^k]刚好大于M,于是,我们要改变C的位置必定在[i+2^(k-1),i+2^k]内,然后对这个区间二分暴力判断。

  好吧,本弱只想到了枚举左端点二分右端点,未曾想到用倍增法进一步确定区间减少二分次数。也算学到了。

const int N=1e6+10;
int n;
ll m,a[N],v[N],b[N],num[N];
bool find(int l,int r)
{
    int len=0;
    ll sum=0;
    for(int i=l; i<=r; i++) b[len++]=a[i];
    sort(b,b+len);
    for(int i=0; i<len; i++)
    {
        sum+=b[i]*v[i];
        if(sum>m) return true;
    }
    return false;
}
int main()
{
    while(~scanf("%d%lld",&n,&m))
    {
        for(int i=0; i<n; i++) scanf("%lld",&a[i]);
        for(int i=0; i<n; i++) scanf("%lld",&v[i]);
        int c=0,j=0;
        for(int i=0; i<n; i++) //枚举左端点,二分右端点
        {
            int k,ans=0,l=i+1,r=n-1;
            for(k=1; k<n; k*=2) if(find(i,i+k)) break;
            l=i+k/2,r=i+k;
            while(l<r)
            {
                int mid=(l+r)/2;
                if(find(i,mid)) r=mid;
                else l=mid+1;
            }
            for(i; i<=l; i++)
            {
                if(i==l)
                {
                    num[i]=++c;
                    break;
                }
                num[i]=c;
            }
        }
        for(int i=0; i<n; i++)
        {
            printf("%d",num[i]);
            if(i!=n-1) printf(" ");
            else printf("\n");
        }
    }
    return 0;
}






转载于:https://www.cnblogs.com/nyist-TC-LYQ/p/7208084.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值