• [hihocoder #1384] Genius ACM 解题报告(倍增)


    题目链接:http://hihocoder.com/problemset/problem/1384

    题目大意:

    给定一个整数 M,对于任意一个整数集合 S,定义“校验值”如下:

    从集合 S 中取出 M 对数(即 2∗M 个数,不能重复使用集合中的数,如果 S 中的整 数不够 M 对,则取到不能取为止),使得“每对数的差的平方”之和最大,这个最大值 就称为集合 S 的“校验值”。

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

    题解:

    现在对于一个数列,它的校验值就是排序之后第一大的和第一小的配对,第二大的和第二小的配对....

    现在我们让段数尽可能少,就是让每段尽可能长

    于是问题就是转化成了确定区间右端点L,左端点最远是哪里的问题

    二分显然是可行的,但是这样常数太大了,我们考虑倍增

    1.初始化p=1,R=L

    2.求出[L,R+p]这段区间的校验值,如果<=k,那么L+=p,p<<=1,否则p>>=1

    3.重复上一步直到p=0退出,更新L=R+1

    怎么求校验值呢?一个显然的方法就是我们每次都排序

    这样显然有大量浪费

    考虑我们每次计算[L,R+p]的时候归并处理,时间复杂度降到$O(nlogn)$

    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #include<cstdio>
    using namespace std;
    typedef long long ll;
    
    const int N=5e5+15;
    int T,n,m;
    ll k;
    ll a[N],b[N],c[N];
    inline ll read()
    {
        char ch=getchar();
        ll s=0,f=1;
        while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
        while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
        return s*f;
    }
    void mergy(int l,int r,int mid)
    {
        int i=l,j=mid;
        for (int k=l;k<=r;k++)
        {
            if ((b[i]<b[j]&&i<mid)||j>r) c[k]=b[i++];
            else c[k]=b[j++];
        }
    }
    int L,R;
    void merge_sort(int l,int r)
    {
        for (int i=R+1;i<=r;i++) b[i]=a[i];
        sort(b+R+1,b+r+1);
        mergy(l,r,R+1);
    }
    ll calc(int l,int r)
    {
        merge_sort(l,r);
        ll re=0,cnt=0;
        for(int i=l,j=r;i<j;i++,j--)
        {
            re+=1ll*(c[i]-c[j])*(c[i]-c[j]);
            cnt++;
            if(cnt==m) break;
        }
        return re;
    }
    int solve()
    {
        int p=1;
        R=L;b[L]=a[L];
        while (p)
        {
            if (R+p<=n&&calc(L,R+p)<=k) 
            {
                R+=p;
                p<<=1;
                for (int i=L;i<=R;i++) b[i]=c[i];
            }
            else p>>=1;
            if (R>n) break;
        }
        return R;
    }
    int main()
    {
        T=read();
        while (T--)
        {
            n=read();m=read();k=read();
            for (int i=1;i<=n;i++) a[i]=read();
            int re=0;
            L=1;
            while (L<=n)
            {
                int k=solve();
                L=k+1;
                re++;
            }
            printf("%d
    ",re);
        }
        return 0;
    }
  • 相关阅读:
    Android中WebView如何加载JavaScript脚本
    Android中WebView如何加载JavaScript脚本
    Android中WebView如何加载JavaScript脚本
    Android如何使用SQLlite数据库
    Android如何使用SQLlite数据库
    Android如何使用SQLlite数据库
    __declspec(dllimport)的作用
    __declspec,__cdecl,__stdcall都是什么意思?有什么作用?
    #pragma pack(push,1)与#pragma pack(1)的区别
    #pragma pack(n) 的作用
  • 原文地址:https://www.cnblogs.com/xxzh/p/9703430.html
Copyright © 2020-2023  润新知