hdu 2688 Rotate (树状数组)

题意:给定一串数字,支持查询和旋转两种操作,查询是查询在经过前面的一些操作之后形成的现在这个序列里的顺序对数,旋转是对给出的[s,t]这一段进行旋转,是将s位置的元素移至t元素的后面,这样会形成一个新串,然后继续操作,直至没有操作。


对于求逆序对数和顺序对数,我们可以想到树状数组来快速求解,因为题目给出的数据量是百万级,而时限是1s,那么O(n^2)是肯定不行的。所以用树状数组我们就可以在给定的时限内完成问题的求解。

我们首先求出给定的最初的数字串的顺序对数。这个操作的时间复杂度是O(n*logn).

然后我们对每个操作进行计算,操作最多m个,每个旋转操作我们用最朴素的旋转,而查询我们就在旋转的时候顺便修改答案值,这样我们的操作时间复杂度就是O(m*(e-s)),因为m*(e-s)的最大值是10^7,等同于O(n).因此我们用这种方法的时间复杂度就是O(n*logn),可以解决此问题。

最朴素的旋转:将值左移,最左的值放到最右边去,其它的值左移一位,此操作只针对区间[s,e]。s,e为数组元素下标。

旋转时修改答案值:将最开始s位置的值存在v中,在将值左移的过程中,看其值是否比v大,大的话答案值减去1,比其小则答案值加上1.


下面附上代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

#define N 10001
#define ll long long
#define Lowbit(x) ((x)&(-x))

ll C[N];
ll num[N*300];
int T;


void add(ll C[],ll pos,ll num) {
    while(pos <= N) {//x最大是N
        C[pos] += num;
        pos += Lowbit(pos);
    }
}

ll Sum(ll C[],ll end) {
    ll sum = 0;
    while(end > 0) {
        sum += C[end];
        end -= Lowbit(end);
    }
    return sum;
}

int main() {
    int n, s, t, i, j, T, k;
    ll ans, tmp;
    while(~scanf("%d",&T)) {
        memset(C,0,sizeof(C));
        ans = 0;
        for(i = 0; i < T; i ++) {
            scanf("%I64d",&num[i]);
            add(C,num[i],1);
            ans += Sum(C,num[i] - 1);
        }
        scanf("%d",&n);
        char c[10];
        while(n--) {
            scanf("%s",c);
            switch(c[0]) {
            case 'Q':
                printf("%I64d\n",ans);
                break;
            case 'R':
                scanf("%d%d",&s,&t);
                int v = num[s];
                for(i = s; i < t; i++){
                    num[i] = num[i + 1];
                    if(v > num[i])ans++;
                    else if(v < num[i])ans--;
                }
                num[t] = v;
                break;
            }
        }

    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值