0x06 倍增

这东西太玄学了我真是不太会。。。

对于这道例题,很容易看出最大值必然是最大减最小,次大减次小……

常规的贪心思想,分的个数一样,总长度越大越好。
其实我的第一想法是二分右端点。。但是只有40,至今没有搞懂为什么倍增会比二分优秀,好玄学。。。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;

int n,m;LL k;
LL a[510000],b[510000],tt[510000];
void mergesort(int l,int r)
{
    if(l==r)return ;
    int mid=(l+r)/2;
    mergesort(l,mid);mergesort(mid+1,r);
    
    int i=l,j=mid+1,p=l;
    while(i<=mid&&j<=r)
    {
        if(b[i]<=b[j])tt[p++]=b[i++];
        else              tt[p++]=b[j++];
    }
    while(i<=mid)tt[p++]=b[i++];
    while(j<=r)  tt[p++]=b[j++];
    
    for(int i=l;i<=r;i++)b[i]=tt[i];
}
int clen;LL c[510000];
bool check(int l,int r)
{
    if(r>n)return false;
    
    int blen=r-l+1;
    for(int i=l;i<=r;i++)b[i-l+1]=a[i];
    mergesort(1,blen);
    
    int i=1,j=1,p=1;
    while(i<=blen&&j<=clen)
    {
        if(b[i]<=c[j])tt[p++]=b[i++];
        else              tt[p++]=c[j++];
    }
    while(i<=blen)tt[p++]=b[i++];
    while(j<=clen)tt[p++]=c[j++];
    
    p--;
    LL sum=0;
    for(int i=1;i<=m;i++)
    {
        if(p-i+1<=i)break;
        sum+=(tt[p-i+1]-tt[i])*(tt[p-i+1]-tt[i]);
    }
    
    if(sum<=k)
    {
        clen=p;
        for(int i=1;i<=clen;i++)c[i]=tt[i];
        return true;
    }
    else return false;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%lld",&n,&m,&k);
        for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
        
        int ed,ans=0;
        for(int st=1;st<=n;st=ed+1)
        {
            ed=st;int L=1;
            clen=0;c[++clen]=a[st];
            while(L>0)
            {
                if(check(ed+1,ed+L)==true)
                {
                    ed=ed+L;
                    L*=2;
                }
                else L/=2;
            }
            ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}
Genius ACM

st表就没什么好说的了,不过就是经常码错板子。。

posted @ 2018-06-28 11:07  AKCqhzdy  阅读(184)  评论(0编辑  收藏  举报