• XJOI 7191 Genius ACM


    二分+倍增

    题目

    题目中的最大校验值应由数组排序后,取出最大值和最小值,次大值和次小值……进行做差平方取和

    所以在加入一个新的数时,校验值是不会下降的

    那么可以发现,校验值是单调递增的,所以可以用二分对每一个固定的左段点找到满足条件的最大的右端点

    所以l初始值设为1,不断对r进行二分,找到最大的点

    进行二分时要用二进制数(倍增),不能直接取mid

    设一个偏量p,右端点即为r+p,一开始设为1,然后对其判断,在k的范围内p就乘2,否则p除以2

    进行判断时,将整个区间进行排序,取前m个数和后m个数分别做差,算出值与k比较

    但此时时间复杂度为O(n*logn*logn),对于n=5*1e5是过不了的

    而排序用的时间最多,所以要利用之前排好的元素进行归并排序

    #include <bits/stdc++.h>
    #define ll long long
    using namespace std;
    const ll MAXN=5*1e5+100;
    ll t,n,m,k,a[MAXN],b[MAXN],p;
    ll ans,c[MAXN];
    bool check(ll l,ll r)
    {
        ll tot=0;
        for (ll i=0;i<m;i++)
        {
            if (l+i>r-i)
              break;
            tot+=(c[l+i]-c[r-i])*(c[l+i]-c[r-i]);
        }
        return tot<=k;
    }
    void merge(ll la,ll ra,ll lb,ll rb)//合并两个有序数组
    {
        ll l,r,now;
        l=la;
        r=lb;
        now=la-1;
        while (l<=ra && r<=rb)
        {
            if (b[l]<=b[r])
            {
                now++;
                c[now]=b[l];
                l++;
            }
            else
            {
                now++;
                c[now]=b[r];
                r++;
            }
        }
        for (ll i=l;i<=ra;i++)
        {
            now++;
            c[now]=b[i];
        }
        for (ll i=r;i<=rb;i++)
        {
            now++;
            c[now]=b[i];
        }
    }
    int main()
    {
        scanf("%lld",&t);
        while (t--)
        {
            scanf("%lld%lld%lld",&n,&m,&k);
            for (ll i=1;i<=n;i++)
              scanf("%lld",&a[i]);
            ll l,r;
            l=1;
            ans=0;
            while (l<=n)
            {
                ans++;//统计答案
                p=1;
                r=l;
                b[l]=a[l];
                while (p)
                {
                    if (r+p>n)
                    {
                        p>>=1;
                        continue;
                    }
                    for (int i=r+1;i<=r+p;i++)//r之前的元素已经排好了序,所以对[r+1,r+p]的元素排序
                      b[i]=a[i];
                    sort(b+r+1,b+r+p+1);
                    merge(l,r,r+1,r+p);//将两个区间的元素合并
                    if (check(l,r+p))
                    {
                        for (int i=l;i<=r+p;i++)//注意,这句不能放在merge函数中,因为p有可能变小,之前排好序的元素可能排到了r之后,在之后统计答案时无法统计到
                          b[i]=c[i];
                        r+=p;
                        p<<=1;
                    }
                    else
                    {
                        p>>=1;
                    }
                }
                l=r+1;
            }
            printf("%lld
    ",ans);
        }
    }
  • 相关阅读:
    解决Odoo出现的Unable to send email, please configure the sender's email address or alias.
    Odoo误删除服务产品造成的错误解决办法
    Linux面试题汇总答案
    win7下安装openpyxl
    在Win7下使用sphinx-build建立开源软件文档
    如何把一个excel工作薄中N个工作表复制到另一个工作薄中
    如何手动添加Windows服务和如何把一个服务删除
    创建用户角色时出现的500错误问题解决方法
    odoo注销后在登录时的用户名和密码
    XenServer6.2详细安装步骤
  • 原文地址:https://www.cnblogs.com/huangchenyan/p/11189370.html
Copyright © 2020-2023  润新知