蒜头君的排序
蒜头君的排序
题目大意
蒜头君是一个爱思考的好孩子,这一天他学习了冒泡排序,于是他就想,把一个乱序排列通过冒泡排序排至升序需要多少次交换,这当然难不倒他,于是他想来点刺激的,给定一个 1 \ldots n1…n 的排列,每次从该排列中选择一个区间 [l,r][l,r],问使用冒泡排序将该区间排至升序需要多少次交换操作。
输入格式
第一行一个整数 nn,表示排列长度。
接下来一行 nn 个整数,表示该排列。
接下来一行一个整数 mm,表示询问次数。
接下来 mm 行,每行 22 个整数 l,rl,r,表示询问 [l,r][l,r] 区间。
输出格式
输出 mm 行,每行 11 个整数,第 ii 行表示第 ii 个询问的答案。
数据规模
对于 3030% 的数据,满足 1 \le n,m \le 3001≤n,m≤300;
对于 6060% 的数据,满足 1 \le n,m \le 10001≤n,m≤1000;
对于 100100% 的数据,满足 1 \le n,m \le 30000,1≤n,m≤30000, l<r,l<r, \sum | l[i]-l[i-1] |\ +∑∣l[i]−l[i−1]∣ + \sum | r[i]-r[i-1] | \le∑∣r[i]−r[i−1]∣≤ 7 \times 10^67×106。
样例输入
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
题解:
这道题要用莫队算法,首先将左端点分块,对于每一块都按右端点从小到大排序。然后维护一个树状数组维护当前区间内比i小的数的个数,特别注意一下临界条件。每次维护一下答案即可。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; typedef long long lol; lol n,m,a[30001],lim,c[30001],cnt[30001]; struct node { lol l,r,id; }s[30001]; lol gi() { lol ans=0,f=1; char i=getchar(); while(i<'0'||i>'9'){if(i=='-')f=-1;i=getchar();} while(i>='0'&&i<='9'){ans=ans*10+i-'0';i=getchar();} return ans*f; } bool cmp(const node a,const node b) { if(a.l/lim!=b.l/lim)return a.l/lim<b.l/lim; else return a.r<b.r; } lol lowbit(lol x) { return x&(-x); } void add(lol x,lol v) { for(lol i=x;i<=n;i+=lowbit(i)) c[i]+=v; } lol getsum(lol x) { lol ans=0; for(lol i=x;i;i-=lowbit(i)) ans+=c[i]; return ans; } int main() { lol i,j; n=gi(); lim=sqrt(n); for(i=1;i<=n;i++) a[i]=gi(); m=gi(); for(i=1;i<=m;i++) { s[i].l=gi(); s[i].r=gi(); s[i].id=i; } sort(s+1,s+m+1,cmp); lol left=1,right=0,ans=0; for(i=1;i<=m;i++) { while(left<s[i].l){add(a[left],-1);ans-=getsum(a[left]-1);left++;} while(left>s[i].l){left--;ans+=getsum(a[left]-1);add(a[left],1);} while(right>s[i].r){ans-=getsum(n)-getsum(a[right]);add(a[right],-1);right--;}// while(right<s[i].r){right++;add(a[right],1);ans+=getsum(n)-getsum(a[right]);}// cnt[s[i].id]=ans; } for(i=1;i<=m;i++) { printf("%lld\n",cnt[i]); } return 0; }