这里先统一一下单点更新以及区间查询问题吧 统一使用全局变量
区间查询的话 目的区间用全局变量x y表示 然后从第一层开始 不断向下层搜(也就是不断的二分的过程)
先说说平时怎么求逆序数把 在每次读入一个数的时候 记录前面比他大的数就是目前的逆序数
那么 怎么用线段树来实现呢 首先 我们需要建一个空树 每个节点记录这个区间里面出现数的个数(sum)然后再按顺序读入数列的时候 查询在读入这个数之前有多个个比他大的数 即查询(x+1,n-1)这个区间有多少值 然后单点更新刚读入的值
#include<cstdio> #include<string.h> #include<iostream> #define maxn 5001 using namespace std; struct node { int l,r,sum; }stu[maxn*4]; int n; int arr[maxn]; int x,y; void build(int l,int r,int i) { stu[i].l=l; stu[i].r=r; stu[i].sum=0; if(l==r) return; int mid=(l+r)/2; build(l,mid,i<<1); build(mid+1,r,i<<1|1); } int que(int i) { if(x<=stu[i].l&&stu[i].r<=y) return stu[i].sum; int mid=(stu[i].l+stu[i].r)/2; int sum1=0,sum2=0; if(x<=mid) sum1=que(i*2); if(y>mid) sum2=que(i*2+1); return sum1+sum2; } void updata(int i) { int l=stu[i].l; int r=stu[i].r; if(l==r) { stu[i].sum++; return; } int mid=(l+r)/2; if(x<=mid) updata(i<<1); else updata(i<<1|1); stu[i].sum=stu[i*2].sum+stu[i*2+1].sum; } int main() { while(cin>>n) { build(0,n-1,1); int sum=0; for(int i=0;i<n;i++) { cin>>arr[i]; x=arr[i]; y=n-1; sum+=que(1);//统计每次插入时候的逆序数 updata(1); } int ans=sum; for(int i=0;i<n;i++) { sum=sum+n-2*arr[i]-1;
/* 因为序列为[0, n-1],若最前面一个数为x,序列中比x 小的数为[0, x-1], 共x个,比x大的数为[x+1, n-1], 共n-x-1个,将x移到最后,比x小的数的逆序数均减1, x的前面比x大的数有n-x-1个,x的逆序数增加n-x-1。 所以新序列的逆序数为原序列的逆序数加上n-2*x-1。 */
if(ans>sum) ans=sum; } cout<<ans<<endl; } return 0;