$CH0601 Genius ACM$ 倍增优化DP

ACWing

Description

 给定一个长度为N的数列A以及一个整数T.我们要把A分成若干段,使得每一段的'校验值'都不超过N.求最少需要分成几段.

Sol

首先是校验值的求法:

要使得'每对数的差的平方'之和最大,显然就是先排序,然后取最大和最小为一对,次大和次小为一对.....

然后是问题的转化:求最少分的段数,显然就是确定左端点后,在校验值不超过T的前提下尽量扩展右端点.

优化就在于右端点的扩展,当然就是用倍增辣qwq

还有就是求校验值的优化:可以不用每次都快排,而是先排增加的一段,然后归并就好了

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#define il inline
#define Rg register
#define go(i,a,b) for(Rg int i=a;i<=b;i++)
#define yes(i,a,b) for(Rg int i=a;i>=b;i++)
#define ll long long
using namespace std;
il int read()
{
    int x=0,y=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    return x*y;
}
int T,n,m,as,a[500001],b[500001],c[500001];
ll K;
il bool ck(int l,int r,int md)
{
    go(i,md,r)b[i]=a[i];
    sort(b+md,b+r+1);
    int i=l,j=md;
    go(k,l,r)
        if((i<=md-1 && b[i]<b[j]) || j>r)c[k]=b[i++];
        else c[k]=b[j++];
    i=l,j=r;int t=min(m,(r-l+1)/2);ll nw=0;
    while(t--)
        nw+=1LL*(c[i]-c[j])*(c[i]-c[j]),i++,j--;
    if(nw<=K)
    {
        go(i,l,r)b[i]=c[i];
        return 1;
    }
    return 0;
}
int main()
{
    T=read();
    while(T--)
    {
        n=read(),m=read();scanf("%lld",&K);as=0;
        go(i,1,n)a[i]=read();
        int l=1,r=1,p=1;
        b[1]=a[1];
        while(l<=n)
        {
            if(r+p<=n && ck(l,r+p,r+1))r+=p,p*=2;
            else p/=2;
            if(!p || r==n){as++,l=++r,p=1,b[l]=a[l];}
        }
        printf("%d
",as);
    }
    return 0;
}
View Code
光伴随的阴影
原文地址:https://www.cnblogs.com/forward777/p/11240241.html