zoukankan      html  css  js  c++  java
  • 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);
        }
    }
  • 相关阅读:
    python 列表 list
    2019书单
    2020书单
    python 字符串
    python 运算符 取余 取商 in not in
    jmeter——安装和运行
    python——CGI编程【 python脚本在Apache上运行前的注意事项】
    python——CGI编程【Apache 支持 python脚本所需要的配置】
    whistle——真机移动端页面调试【在页面注入js实现模拟PC浏览器的Console功能】
    whistle——真机移动端页面调试【查看、修改真机端的页面DOM结构及样式】
  • 原文地址:https://www.cnblogs.com/huangchenyan/p/11189370.html
Copyright © 2011-2022 走看看