HihoCoder - 1384 Genius ACM ( 倍增+归并 )

链接: https://cn.vjudge.net/contest/250027#problem/B

题意: 题意就很恶心。 现在给你一个长度为n的序列,将原序列分成尽量小的块(不改变顺序)使得满足下列情况:  要求在每一块中任意取得m对数,(2*m个),不够m对,尽量多取,并且每个数只能用一次。使得满足每一对的差值的平方的和<=K.问你最小块数是多少?

思路参考:坤神博客:https://blog.csdn.net/nka_kun/article/details/82632471

思路: 最坏的情况 一定是这个块里的(最大和最小的差的平方)+(次大和次小的平方)........。

那么这样我可以枚举左端点,那我要做的就是怎么确定右端点,很容易想 到的就是二分,然后judge,但是肯定是不行的,因二分的复杂度是n*n*logn,因为每次judge就是一个o(n) 。这里我可以从左端点开始倍增,不断的去扩大右端点,这样的话,我每次的judge函数中的判断就肯定会比二分小很多很多。对于起始长度为len=1 ,如果r+len满足条件那么r更新为r+len,并且len*=2,如果不满足条件,那么将len缩小直至满足条件,如果len==0||r>=n 就跳出。

代码:

#include

using namespace std;
typedef long long ll;
const int N =5e5+5;

ll a[N],b[N],c[N],d[N];
int n,m;
ll K;

int jud(int len1,int len2)
{
    int p1,p2;
    p1=1; p2=1;
    int tot=0;
    while(p1<=len1&&p2<=len2){
        if(b[p1]<=c[p2]){
            d[++tot]=b[p1];
            p1++;
        }
        else{
            d[++tot]=c[p2];
            p2++;
        }
    }
    while(p1<=len1){
        d[++tot]=b[p1++];
    }
    while(p2<=len2){
        d[++tot]=c[p2++];
    }

    int pl,pr;
    ll sum=0;
    pl=1; pr=tot;
    int cnt=0;
    while(plK) return 0;
        pl++; pr--;
    }
    if(sum>K) return 0;
    for(int i=1;i<=tot;i++) b[i]=d[i];
    return 1;
}

int solve(int id)
{
    int l,r,len;
    l=id; r=id; len=1;
    b[1]=a[id];
    int tot;
    while(1)
    {
        if(r+len>n) len=n-r;
        tot=0;
        for(int i=r+1;i<=r+len;i++){
            c[++tot]=a[i];
        }
        sort(c+1,c+tot+1);
        if(jud(r-l+1,len)){
            r+=len; len*=2;
        }
        else{
            len/=2;
        }
        if(len==0) break;
        if(r>=n) break;
    }
    return r;
}

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 ans=0;
        for(int i=1;i<=n;)
        {
            i=solve(i)+1;
            ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

你可能感兴趣的:(数据结构,思维)