“玲珑杯”ACM比赛 Round #13 B -- 我也不是B(二分排序)

传送门

题意

开始有一个空序列s,一个变量c=0,接着从左往右依次将数组a中的数字放入s的尾部,每放一个数字就检测一次混乱度K,当混乱度k大于M时就清空序列并让c=c+1,K = Bi * Vi(1<=i<=k(序列总长度)的总和),Bi表示序列中第i小的数字,Vi是给定的非递减的数,输出每次加入序列后的变量c

分析

令当前左端点为L, 我们先找到一个最小的K, 使得[L,L+2K)这个区间混乱度超过M
然后右端点在[L+2K−1,L+2K)中二分即可
考虑每删去至少2K−1
个数, 所需要的时间复杂度最大为O(2KKlogN)
故总复杂度为O(Nlog2N)

trick

代码

#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define F(i,a,b) for(int i=a;i<=b;++i)
#define R(i,a,b) for(int i=a;i<b;++i)
#define mem(a,b) memset(a,b,sizeof(a))
#pragma comment(linker, "/STACK:102400000,102400000")
inline void read(int &x){x=0; char ch=getchar();while(ch<'0') ch=getchar();while(ch>='0'){x=x*10+ch-48; ch=getchar();}}

int n;
ll m;
int a[300300],v[300300],Pow[25],temp[300300];
int ans[300300];
bool judge(int l,int r)
{
    ll sum=0LL;
    int cnt=0;
    for(int i=l;i<=r;++i) temp[cnt++]=a[i];
    sort(temp,temp+cnt);
    for(int j=0;j<cnt;++j) sum+=(ll)temp[j]*v[j];
    if(sum>m) return 1;return 0;
}
int erfen(int l,int left,int right)
{
    while(left<right)
    {
        int mid=(left+right>>1);
        if(judge(l,mid)) right=mid;else left=mid+1;
    }
    return left;
}
int check(int l,int n)
{
    int left=l,right=l;
    for(int i=0;;++i,right+=Pow[i])
    {
        if(right>n) right=n;
        if(judge(l,right)) break;
        left=right+1;
        if(right==n) break;
    }
    return erfen(l,left,right);
}
int main()
{
    Pow[0]=1;
    F(i,1,24) Pow[i]=Pow[i-1]*2;
    while(scanf("%d %lld",&n,&m)!=EOF)
    {
        F(i,0,n-1) scanf("%d",a+i);
        F(i,0,n-1) scanf("%d",v+i);
        mem(ans,0);
        for(int i=0;i<n;)
        {
            int j=check(i,n);
            ans[j]=ans[i==0?0:i-1]+1;
            i=j+1;
        }
        for(int i=1;i<n;++i) ans[i]=max(ans[i],ans[i-1]);
        for(int i=0;i<n;++i) printf("%d%c",ans[i],i==n-1?'\n':' ');
    }
    return 0;
}
posted @ 2017-05-23 16:03  遗风忘语  阅读(116)  评论(0编辑  收藏  举报