蒜头君的排序(sort)
2000ms 262144K
蒜头君是一个爱思考的好孩子,这一天他学习了冒泡排序,于是他就想,把一个乱序排列通过冒泡排序排至升序需要多少次交换,这当然难不倒他,于是他想来点刺激的,给定一个1…n1的排列,每次从该排列中选择一个区间[l,r],问使用冒泡排序将该区间排至升序需要多少次交换操作。
输入格式
第一行一个整数n,表示排列长度。
接下来一行n个整数,表示该排列。
接下来一行一个整数m,表示询问次数。
接下来m 行,每行2个整数l,r,表示询问[l,r] 区间。
输出格式
输出m行,每行1个整数,第i行表示第i个询问的答案。
数据规模
对于30%的数据,满足1≤n,m≤300;
对于60%的数据,满足1≤n,m≤1000;
对于100%的数据,满足1≤n,m≤30000,l<r,l<r,∑∣l[i]−l[i−1]|+∑∣r[i]−r[i−1]∣≤ 7*10^6。
样例输入
样例输入
10
9 8 7 4 5 6 10 3 2 1
5
2 4
8 10
2 8
5 9
4 9
样例输出
3
3
13
7
9
解析:这道题直接暴力(直接用冒泡排序或归并排序记录逆序对)可以拿60分;
离线使用树状数组维护能得70分;
在线维护树状数组可以拿100分。。。(为什么离线会超时。。)
关于树状数组的维护我们可以先定义两个指针分别指向左端点和右端点,每次查询时不断的维护这段区间(左端点,右端点不断的向左向右移),即可查询到所有的答案(好神奇。。)
十分不解为什么不能先按照左端点的位置排序,后维护,这样可以少进行一个左端点向左的步骤。。可惜会超时;
关于如何从[l,r]变成[l-1,r],[l+1,r],[l,r-1],[l,r+1],自己手动模拟一遍就知道了,第一次感觉好玄学。。。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define lo(x) (x&(-x))
#define ll long long
#define man 30010
using namespace std;
//common
int n,m,c[man],pos[man],ask[man];
ll a[man],b[man];
inline int read()
{ int x=;bool f=;
char ch=getchar();
while(ch<''||ch>''){f=(ch==);ch=getchar();}
while(ch>=''&&ch<=''){x=(x<<)+(x<<)+(ch^);ch=getchar();}
return f?(~x+):x;
}
struct range
{ int x,y,id;}e[man];
//lowbit
inline void add(int x,int val)
{ while(x<=n)
{ c[x]+=val;
x+=lo(x);
}
return ;
}
inline int calc(int x)
{ int ans=;
while(x>)
{ ans+=c[x];
x-=lo(x);
}
return ans;
}
int main()
{ freopen("sort.in","r",stdin);
freopen("sort.out","w",stdout);
n=read();
for(int i=;i<=n;i++)
scanf("%lld",&a[i]),b[i]=a[i];
sort(a+,a++n);
for(int i=;i<=n;i++)
pos[i]=lower_bound(a+,a+n+,b[i])-a;
m=read();
for(int i=;i<=m;i++)
{ e[i].x=read();e[i].y=read();e[i].id=i;}
int l=,r=,ans=;
for(int i=;i<=m;i++)
{ while(r<e[i].y)
{ r++;
ans+=calc(n)-calc(pos[r]-);
add(pos[r],);
}
while(l<e[i].x)
{ add(pos[l],-);
ans-=calc(pos[l]-);
l++;
}
while(r>e[i].y)
{ add(pos[r],-);
ans-=calc(n)-calc(pos[r]-);
r--;}
while(l>e[i].x)
{ l--;
ans+=calc(pos[l]-);
add(pos[l],);
}
ask[e[i].id]=ans;
}
for(int i=;i<=m;i++)
printf("%d\n",ask[i]);
return ;
}