玲珑杯#round13 我也不是B(倍增+二分查找)

来源:互联网 发布:404什么意思网络用语 编辑:程序博客网 时间:2024/05/20 14:20

原题链接:http://www.ifrog.cc/acm/problem/1112

题意:

一个无序数列,从左至右依次挑出放到另外一个空的数列中,若是当前数列的混乱度大于特定的值,计数器加一,数列清空。

思路:n的范围较小,暴力枚举左端点,二分右端点,找到一个最小的k,使得当前左端点L和L+2^k之间存在某个值,

使得当前数列的混乱度超过M,并且,一定在L+2^k-1和L+2^K之间。(不然的话,k就不会是k,而是k-1了)。

但是,二分查找有序数列非常快,而原数列是无序数列,所以如此大的数据范围,就要适当缩小一点。怎么缩小呢。

倍增算法,在这里用来找一个k的值,一个for循环就结束了他的作用。

代码:

#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#include<iostream>#include<map>#include<set>#include<vector>#include<string>using namespace std;typedef long long ll;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;}


0 0
原创粉丝点击