P1908 逆序对(树状数组)

题目传送门
对于逆序对,可以用树状数组来求,而树状数组就是求出所有的非逆序对,答案当然就是所有情况减去非逆序对了。
举个栗子
1 3 5 2 4
首先输入1,那么在树状数组角标1的位置加入1,3,5同理
这样在加入3的时候,求出前面有一个和它非逆序
5的时候发现前面有两个数和它非逆序
2的时候发现前面有一个数和它非逆序
4的时候发现前面有三个和它非逆序
那么答案就是从5个数中选两个的组合数-非逆序对数
就是10-7=3

树状数组的一些操作:
l o w b i t lowbit lowbit操作

ll lowbit(ll x)
{
	return x&(-x);
} 

更新操作

void update(ll i, ll x)//更新操作 
{
    for (ll pos = i; pos < MAXN; pos += lowbit(pos))
        tree[pos] += x;
}

求前n项和操作

ll query(ll n)//求前n项和 
{
    ll ans = 0;
    for (ll pos = n; pos; pos -= lowbit(pos))
        ans += tree[pos];
    return ans;
}

求区间和操作

ll query(ll a, ll b)//查询 
{
    return query(b) - query(a - 1);
}

本题有一些问题就是:
1.为什么会RE,刚开始做的时候,内心看到满屏的RE是崩溃的。
因为内容的数字可能会大于1e9但是正常的数组没有办法开到如此大,因此需要离散化
2.在离散化过程中如果遇到两数相等会不会出问题
那么就需要在离散化过程中的cmp函数的作用了,如果数值相等,那么这个就不能说是逆序对了,所以需要id小的放到前面去,id大的放到后面去(离散化记录的是相对的位置大小),这样处理的话相等的数值就不会出现误判逆序的问题了。

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll MAXN=6e5;
ll tree[MAXN];
struct node{
	ll id;
	ll t;
}s[2100021];
ll a[2102010];
ll lowbit(ll x)
{
	return x&(-x);
} 
void update(ll i, ll x)//更新操作 
{
    for (ll pos = i; pos < MAXN; pos += lowbit(pos))
        tree[pos] += x;
}
ll query(ll n)//求前n项和 
{
    ll ans = 0;
    for (ll pos = n; pos; pos -= lowbit(pos))
        ans += tree[pos];
    return ans;
}
ll query(ll a, ll b)//查询 
{
    return query(b) - query(a - 1);
}
ll cmp(node x,node y){
	if(x.t==y.t)return x.id<y.id;
	return x.t<y.t;
}
int main(){
	ll n;
	scanf("%lld",&n);
	ll p=0;ll u;
	for(ll i=1;i<=n;i++){
		scanf("%lld",&s[i].t);
		s[i].id=i;
	}
	sort(s+1,s+1+n,cmp);
	for(ll i=1;i<=n;i++){
		a[s[i].id]=i;
	}
	for(ll i=1;i<=n;i++){
		p+=query(a[i]);
		update(a[i],1);
	}
	ll ans=n*(n-1)/2;
	cout<<ans-p<<endl;
}

还是挺考察对树状数组的理解的,好题啊!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值