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\n",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\n",ans);
    }
    return 0;
}
posted @ 2020-07-24 16:12  shuitiangong  阅读(106)  评论(0编辑  收藏  举报