啊这道题思维比较巧妙,首先用线段树单点更新从后往前求得每一位的逆序数个数,相加得出逆序数的总和。然后就是要思考的东西了:从一个序列转换为另一个序列时,把数组的第一个元素移到数组尾部,那么这个时候逆序数的变化是多少呢:-a[i]+(n-1-a[i]);为什么呢,因为这时候所有的元素都在它的后面,把它从第一位移到最后那么减少的逆序数自然是这个时候排在它后面而且比它小的元素个数,同理增加的逆序数就是排在它后面比它大的元素个数,然后求最值就好了。楼主因为脑残忘记每次ans置零了WA了一发,大家不要学习ORZ~:
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<vector>
#include<map>
#include<algorithm>
#include<queue>
#include<stack>//线段树单点更新查询 求逆序数
using namespace std;
typedef long long ll;
#define N 5100
int g[4*N];
int low[N];
int a[N];
int ans[N];
void init(int rt, int l, int r)
{
if(l == r)
{
g[rt] = 0;
return;
}
int mid = (l+r)>>1;
init(rt<<1, l, mid);
init(rt<<1|1, mid+1, r);
g[rt] = 0;
}
int find(int rt, int l, int r, int L, int R)
{
if(l >= L && r <= R)
{
return g[rt];
}
int mid = (l+r)>>1;
int k1 = 0, k2 = 0;
if(mid >= L)
{
k1 = find(rt<<1, l, mid, L, R);
}
if(mid < R)
{
k2 = find(rt<<1|1, mid+1, r, L, R);
}
return k1+k2;
}
void Update(int rt, int l, int r, int x)
{
if(l == r && l == x)
{
g[rt]++;
return;
}
int mid = (l+r)>>1;
if(x <= mid)
{
Update(rt<<1, l, mid, x);
g[rt]++;
}
if(x > mid)
{
Update(rt<<1|1, mid+1, r, x);
g[rt]++;
}
}
int main()
{
int t, ans1;
while(~scanf("%d", &t))
{
int ans1 = 0;
memset(ans, 0, sizeof(ans));
memset(low, 0, sizeof(low));
for(int i = 1; i <= t; i++)
{
scanf("%d", &a[i]);
a[i]++;
}
init(1, 1, t);
for(int i = t; i >= 1; i--)
{
if(a[i] == 1)
low[i] = 0;
else
low[i] = find(1, 1, t, 1, a[i]-1);
ans1 += low[i];
Update(1, 1, t, a[i]);
}
//printf("%d\n", ans1);
ans[0] = ans1;
for(int i = 1; i <= t; i++)
{
ans[i] = ans[i-1]-a[i]+(t-a[i])+1;
ans1= min(ans[i], ans1);//每次更新的时候,这个元素都是在数组的第一个
//这个元素移到最后,则逆序数的变化为,减少了a[i]-1个,增加了(t-a[i]个)
//则转移方程为 ans[i] = ans[i-1]-(a[i]-1)+(t-a[i]);
//printf("%d\n", ans[i]);
}
printf("%d\n", ans1);
}
return 0;
}