zoukankan      html  css  js  c++  java
  • AcWing 109 天才ACM(倍增)

    题目链接

    基本思路

      这道题最基本的想法就是一次寻找每个区间,对于每个区间,用二分来判定其最大长度。每次check的时候,对区间排序,不断取出不大于m对最大值与最小值求值即可。
      然后你就喜提TLE了,笑如果用倍增来代替二分的话能过,不过其实倍增最坏复杂度和二分一样,应该是数据没有刻意来卡倍增。下面先给出倍增的代码。

    代码

    const int maxn = 5e5+10;
    int t,n,m,arr[maxn],tmp[maxn]; ll T;
    ll check(int l, int r) {
        for (int i = l; i<r; ++i) tmp[i] = arr[i];
        sort(tmp+l,tmp+r);
        ll sum = 0; int stp = 0;
        for (int i = 0; i < m && l+i<r-i-1; i ++ )
            sum += (ll)(tmp[l+i]-tmp[r-i-1])*(tmp[l+i]-tmp[r-i-1]);
        return sum;
    }
    int main(){
        scanf("%d",&t);
        while(t--) {
            scanf("%d%d%lld",&n,&m,&T);
            for (int i = 0; i<n; ++i) scanf("%d",&arr[i]);
            int st = 0, ed = 0, ans = 0;
            while(ed<n) {
                int len = 1;
                while(len) {
                    if (ed+len<=n && check(st,ed+len)<=T) {
                        ed += len; len <<= 1;
                    }
                    else len >>= 1;
                }
                ++ans;
                st = ed;
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    

    进一步优化

      我们在倍增的时候发现一个问题,每次枚举区间的时候,它的长度都是在递增的,而我们每次check都重复的对区间前部进行的排序,所以就有了优化空间。
      我们可以每次只对新增的部分排序,因为前面的部分有序,所以就可以像归并排序那样讲两段区间合并成一个区间,这样减少了排序长度,就能获得更快的速度。

    代码

    const int maxn = 5e5+10;
    int t,n,m,arr[maxn],tmp[maxn],tmp2[maxn]; ll T;
    bool check(int l, int r, int len) {
        for (int i = l; i<r+len; ++i) tmp[i] = arr[i];
        sort(tmp+r,tmp+r+len);
        int st1 = l, st2 = r, tot = l;
        while(st1<r || st2<r+len) {
            if (st2>=r+len || (st1<r&&tmp[st1]<=tmp[st2])) tmp2[tot++] = tmp[st1++];
            else tmp2[tot++] = tmp[st2++];  
        }
        ll sum = 0;
        for (int i = 0; i < m && l+i<r+len-i-1; ++i)
            sum += (ll)(tmp2[l+i]-tmp2[r+len-i-1])*(tmp2[l+i]-tmp2[r+len-i-1]);
        return sum<=T;
    }
    int main(){
        scanf("%d",&t);
        while(t--) {
            scanf("%d%d%lld",&n,&m,&T);
            for (int i = 0; i<n; ++i) scanf("%d",&arr[i]);
            int st = 0, ed = 0, ans = 0;
            while(ed<n) {
                //cout << st << ' ' << ed << endl;
                int len = 1;
                while(len) {
                    if (ed+len<=n && check(st,ed,len)) {
                        for (int i = ed; i<ed+len; ++i) arr[i] = tmp2[i];
                        ed += len; len <<= 1;
                    }
                    else len >>= 1;
                }
                ++ans;
                st = ed; 
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    Springboot日志初探
    Slf4j初探
    Log4j日志初探
    Nacos集群初探
    python初学者必看学习路线图!!!
    ubuntu无法设置为中文怎么办?(适用于ubuntu14.04/16.04)
    PythonGUI编程(Tkinter)-基本概念以及核心开发步骤
    Python学生信息管理系统(注释最详细,小白都看的懂)
    Python面向对象分析存放家具
    面向对象分析烤地瓜项目
  • 原文地址:https://www.cnblogs.com/shuitiangong/p/13372530.html
Copyright © 2011-2022 走看看