1112 - 我也不是B



DESCRIPTION

小 L 有一串 QQ 个数排成一排,从左往右是 A1A1 开始一直到 AQAQ,它一直很喜欢这串数。

有一天,小 L 去高考报名了,小 J 和小 N 过来用这串数玩游戏。

一开始,小 N 手上有一个空序列,定义变量C:=0C:=0。小 J 从左往右依次把序列中的数取出,放到小 N 手上序列的最右边,如果放完之后,整个序列的混乱度超过了 MM,小 N 就会把手上的所有数全部扔掉,然后令 C:=C+1C:=C+1

定义一个长度为 KK 序列的混乱度 S=Ki=1Bi×ViS=∑i=1KBi×Vi,其中 BiBi 表示序列中第 ii 小的数,VV 为一个给定的序列。

小 J 和小 N 想知道,加入每个数之后,CC 是多少。

INPUT
第一行两个整数 QQ MM接下来一行 QQ个整数, 第 ii个整数表示 AiAi接下来一行 QQ个整数, 第 ii个整数表示 ViVi
OUTPUT
一行 QQ个整数, 第 ii个整数表示加入第 ii个数之后的 CC, 相邻两个整数之间用空格隔开, 注意最后一个数后不要输出空格输出完请换行
SAMPLE INPUT
5 11 3 2 5 41 1 1 1 1
SAMPLE OUTPUT
0 1 2 3 4
HINT
1Q300000,0M1018,1Ai108,1Vi1041≤Q≤300000,0≤M≤1018,1≤Ai≤108,1≤Vi≤104对于所有的 1iQ1,ViVi+11≤i≤Q−1,Vi≤Vi+1


B

一个显然的想法是每一次二分加入到第几个数的时候, 混乱度超过MM, 然后暴力检验, 复杂度显然可以是O(N2logN)O(N2logN)级别

我们可以换一种二分方式

令当前左端点为LL, 我们先找到一个最小的KK, 使得[L,L+2K)[L,L+2K)这个区间混乱度超过MM

然后右端点在[L+2K1,L+2K)[L+2K−1,L+2K)中二分即可

考虑每删去至少2K12K−1个数, 所需要的时间复杂度最大为O(2KKlogN)O(2KKlogN)

故总复杂度为O(Nlog2N)



当初想的时候就觉得如果M大,C增加的就少,中间算的就不多,如果M小,排序的数字就小。怎么都想不明白,双二分。很经典。


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=300000;
int ans[300005];
ll a[300005];
ll v[300005];
ll tmp[300005];
int p[29];
void init()
{
    p[0]=1;
    for(int i=1; i<=25; i++)
        p[i]=p[i-1]*2;
}
int judge(int l,int r,ll m)
{
    ll sum=0;
    int cnt=0;
    for(int i=l; i<=r; i++)
        tmp[cnt++]=a[i];
    sort(tmp,tmp+cnt);
    for(int i=0;i<cnt;i++)
        sum+=tmp[i]*v[i];
    if(sum>m)
        return 1;
    return 0;
}
int g(int l,int r1,int r2,ll m)
{
    while(r1<r2)
    {
        int midr=(r1+r2)>>1;
        if(judge(l,midr,m))
            r2=midr;
        else
            r1=midr+1;
    }
    return r1;
}
int check(int l,int n,ll m)
{
    int r1=l;
    int r2=l;
    for(int i=0;; i++,r2+=p[i])
    {
        if(r2>n)
            r2=n;
        if(judge(l,r2,m))
            break;
        r1=r2+1;
        if(r2==n)
            break;
    }
    return g(l,r1,r2,m);
}
void f(int n,ll m)
{
    int i=0;
    while(i<n)
    {
        int j=check(i,n,m);
        ans[j]=ans[i==0?0:i-1]+1;
        i=j+1;
    }
    for(i=1; i<n; i++)
    {
        ans[i]=max(ans[i-1],ans[i]);
    }
}
int main()
{
    int n;
    ll m;
    init();
    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]);
        memset(ans,0,sizeof(ans));
        f(n,m);
        for(int i=0; i<n; i++)
            printf("%d%c",ans[i],i==n-1?'\n':' ');
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值