题目大意:
随机给你全排列中的一个,但不断的把第一个数丢到最后去,重复N次,形成了N个排列,问你这N个排列中逆序数最小为多少
做法:
逆序数裸的是N^2 利用线段树可以降到NlogN
具体方法是插入一个数,查询之前比他大的数有多少个,即query(A[i]+1,N,1,N,1),利用线段树保存区间和即可;
至于不断把一个数丢到最后,可以用O(1)的时间来处理 即 temp=temp-A[i]+(N-A[i]-1); 这个地方很容易思考的 不解释了
完整代码如下:
/* 1.WA 忘记初始化线段树 */ #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <ctime> #include <algorithm> #include <iostream> #include <sstream> #include <string> #define oo 0x13131313 using namespace std; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define maxn 5555 int tree[maxn*4]; int N; int A[maxn]; void input() { for(int i=1;i<=N;i++) scanf("%d",&A[i]); } int PushUp(int rt) { tree[rt]=tree[rt<<1]+tree[rt<<1|1]; } int updata(int p,int k,int l,int r,int rt) { int m; if(l==r) {tree[rt]+=k;return 0;} m=(l+r)>>1; if(p<=m) updata(p,k,lson); else updata(p,k,rson); PushUp(rt); } int query(int L,int R,int l,int r,int rt) { int temp=0,m; if(L<=l&&r<=R) return tree[rt]; m=(l+r)>>1; if(L<=m) temp=temp+query(L,R,lson); if(R>m) temp=temp+query(L,R,rson); return temp; } void solve() { int ans=0x3f3f3f3f; int temp=0; for(int i=1;i<=N;i++) { temp=temp+query(A[i]+1,N,1,N,1); updata(A[i],1,1,N,1); } ans=temp; for(int i=1;i<=N-1;i++) { temp=temp-A[i]+(N-A[i]-1); if(temp<ans) ans=temp; } printf("%d ",ans); } int main() { while(cin>>N) { memset(tree,0,sizeof(tree)); input(); solve(); } }