Genius ACM


题目描述

给定一个整数 M,对于任意一个整数集合 S,定义“校验值”如下:
从集合 S 中取出 M 对数(即 2∗M 个数,不能重复使用集合中的数,如果 S 中的整 数不够 M对,则取到不能取为止),使得“每对数的差的平方”之和最大,这个最大值 就称为集合 S 的“校验值”。
现在给定一个长度为 N 的数列 A 以及一个整数 T。我们要把 A 分成若干段,使得 每一段的“校验值”都不超过T。求最少需要分成几段。

分析

  • 求校验值显然是要让最大的数和最小的数组合起来
  • 从数组第一个数开始,求出包含他的集合最大是多大(利用倍增),然后跳到下一个位置继续求。

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <map>
using namespace std;
const int maxn=500050;
typedef long long ll;
namespace Input
{
    const int BUF = 65536;
    char buf[BUF + 1];
    char *head = buf, *tail = buf;
}
inline char inputchar()
{
    using namespace Input;
    if(head == tail)
        *(tail = (head = buf) + fread(buf, 1, BUF, stdin)) = 0;
    return *head++;
}
inline void input(int &ret)
{
    char ch = inputchar();
    while(ch < '0' || ch > '9')
        ch = inputchar();
    ret = ch - '0';
    ch = inputchar();
    while(ch >= '0' && ch <= '9')
    {
        ret = ret * 10 + ch - '0';
        ch = inputchar();
    }
}
inline void input(ll &ret)
{
    char ch = inputchar();
    while(ch < '0' || ch > '9')
        ch = inputchar();
    ret = ch - '0';
    ch = inputchar();
    while(ch >= '0' && ch <= '9')
    {
        ret = ret * 10 + ch - '0';
        ch = inputchar();
    }
}
int a[maxn];
int b[maxn];
int c[maxn];
int n,m;
ll k;
inline ll f(int l,int mid,int r){
    int sz=0;
    for(int i = mid+1; i <= r; ++i) b[sz++]=a[i];
    sort(b,b+sz);
    int i=l,j=0;
    int tot=0;
    while(1) {
        if(i<=mid&&j<sz&&a[i]<b[j]) c[tot++]=a[i],i++;
        else if(i<=mid&&j<sz) c[tot++]=b[j],j++;
        else if(i<=mid) c[tot++]=a[i],i++;
        else if(j<sz) c[tot++]=b[j],j++;
        else break;
    }
    ll ans=0;
    for(int i = 0; i < m&&i <= tot/2-1; ++i) {
        ans+=(ll)(c[i]-c[tot-i-1])*(c[i]-c[tot-i-1]);
    }
    if(ans<=k) {
        for(int i = 0; i < tot; ++i) a[l+i]=c[i];
    }
    return ans;
}
int main() {
    int t;
    //scanf("%d", &t);
    input(t);
    while(t--) {
        input(n);
        input(m);
        input(k);
        for(int i = 1; i <= n; ++i) input(a[i]);// scanf("%d", a+i);
        register int i=1,ans=0;
        register int j=i,p=1;
        while(i<=n) {
            j=i,p=1;
            while(i+p<=n&&f(i,i+(p>>1),i+p)<=k) j=i+p,p<<=1;
            p>>=1;
            while(p) {
                if(j+p<=n&&f(i,j,j+p)<=k) j+=p;
                p>>=1;
            }
            //printf("%d %d\n", i,j);
            i=j+1;
            ans++;
        }
        printf("%d\n", ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/sciorz/p/9320606.html
ACM