线段树专题#5_蒟蒻训练历程记录_HDU 1394 Minimum Inversion Number_单点更新+思维转换

               啊这道题思维比较巧妙,首先用线段树单点更新从后往前求得每一位的逆序数个数,相加得出逆序数的总和。然后就是要思考的东西了:从一个序列转换为另一个序列时,把数组的第一个元素移到数组尾部,那么这个时候逆序数的变化是多少呢:-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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值