题意:给定一串数字,支持查询和旋转两种操作,查询是查询在经过前面的一些操作之后形成的现在这个序列里的顺序对数,旋转是对给出的[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;
}