本题的意思是求逆序数,可以将前m个数放到序列最后面,求逆序数的最小值
方法就是找出每个数之前有多少个比它大的数累加即可,处理时可以用树状数组,也可以用线段树,我用的是线段树
先建一棵空树,依次处理每个元素,把线段树的对应节点a[i]++,然后在原序列中,在a[i]前面且比它大的就是在线段树中
a[i+1]到n这段区间和了;
这样容易找原序列的逆序数,至于调动是有个规律的,每次把第一个移到后面,逆序数就等于原来的-(后面比a小的)+(比a大的)
代码如下
#include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> using namespace std; #define inf 0x3f3f3f3f int a[5005]; int tree[5005*4]; void build(int n,int l,int r) { if(l==r) { tree[n]=0; return; } int m=(l+r)>>1; build(n<<1,l,m); build(n<<1|1,m+1,r); tree[n]=tree[n<<1]+tree[n<<1|1]; } void update(int n,int l,int r,int pos) { if(l==r) { tree[n]++; return; } int m=(l+r)>>1; if(pos<=m) update(n<<1,l,m,pos); else update(n<<1|1,m+1,r,pos); tree[n]=tree[n<<1]+tree[n<<1|1]; } int query(int n,int l,int r,int ll,int rr) { if(ll<=l&&rr>=r) { return tree[n]; } int ans=0; int m=(l+r)>>1; if(ll<=m) ans+=query(n<<1,l,m,ll,rr); if(rr>m) ans+=query(n<<1|1,m+1,r,ll,rr); return ans; } int main() { int n; while(~scanf("%d",&n)) { for(int i=1; i<=n; i++) { scanf("%d",&a[i]); a[i]++; } build(1,1,n); int cnt=0; for(int i=1; i<=n; i++) { update(1,1,n,a[i]); cnt+=query(1,1,n,a[i]+1,n); } int mx=cnt; for(int i=1; i<=n; i++) { cnt=cnt-(a[i]-1)+(n-a[i]); mx=min(mx,cnt); } printf("%d\n",mx); } return 0; }

616

被折叠的 条评论
为什么被折叠?



