7-4 运动的球球——双树状数组

本文介绍了一种解决大范围数值问题的策略,通过离散化和树状数组的双重应用,计算数组a中元素满足a[i]≤a[j]的数对(i,j)位置差之和,适用于n<10^5且元素范围-1e8到1e8的数据。作者还提供了详细的代码实现和关键步骤解析。
摘要由CSDN通过智能技术生成

7-4 运动的球球


题意:

题意转化过来为:
给定一个长度为 n 的数组 a i a_i ai,找到所有满足 a [ i ] ≤ a [ j ] a[i] ≤ a[j] a[i]a[j] 的数对 ( i , j ) (i,j) (i,j) 的位置差之和。
2 < n < 1 0 5 , − 1 0 8 ≤ a [ i ] ≤ 1 0 8 . 2<n<10^5, -10^8 ≤ a[i]≤ 10^8. 2<n<105,108a[i]108.

分析:

如果这道题求的是:所有满足 a [ i ] ≤ a [ j ] a[i] ≤ a[j] a[i]a[j] 的数对 ( i , j ) (i,j) (i,j)个数,其中 1 ≤ a [ i ] ≤ 1 e 5 1≤a[i]≤1e5 1a[i]1e5

那么像求逆序对那样在数值范围上建立树状数组,从前到后遍历每个位置,对于每个位置 i 求当前树状数组中位置区间 [ 1 , a [ i ] ] [1, a[i]] [1,a[i]] 中的数和,然后把树状数组的位置 a[i] 上的数+1。

那么,如果在这个数据范围下,求满足数对的位置差之和的话:
可以将 a[i] 所对应的树状数组位置上每次加的 1 变成 i,那么每次查询就能找到前面所有位置中,小于等于当前值的位置之和 sum。(注意是位置之和sum(i),不是个数)

那么已知前面满足的位置之和,如何求和当前位置的位置差之和呢?
只需要再知道前面满足小于等于当前值的位置个数 cnt,用 当前位置i*位置个数cnt - 前面满足的位置之和sum 便是位置差之和了。

求小于等于当前值的位置个数就像前面说的,树状数组位置 a[i] 上的数+1。

所以就需要用两个树状数组来操作,一个求位置之和,一个求位置个数。

但是这道题的 a[i] 数据范围很大,在数值范围上建立树状数组开不了这么大,但是一共只有 1e5 个数,所以可以离散化。

和我昨天做的那道求 合适数对 有异曲同工之妙。但是这道题做出来啦~

Code:
#include<bits/stdc++.h>
using namespace std;

#define Ios ios::sync_with_stdio(false),cin.tie(0)
#define mem(a,b) memset(a,b,sizeof a)
#define int long long
#define PII pair<int,int>
#define pb push_back
#define fi first
#define se second
#define endl '\n'

map<int,int> mp;

const int N = 100010, mod = 1e9+7;
int T, n, m, k;
vector<int> ve;

int get(int x){
	return lower_bound(ve.begin(), ve.end(), x) - ve.begin() + 1;
}

struct node{
	int pos, v;
}a[N];

int c1[N], c2[N];

int lbit(int x){
	return x & -x;
}

bool cmp(node a, node b){
	return a.pos < b.pos;
}

void add(int x, int y){
	for(int i=x;i<=n;i+=lbit(i)) c1[i]+=y, c2[i]++;
}

int qsum(int x){
	int ans=0;
	for(int i=x;i;i-=lbit(i)) ans+=c1[i];
	return ans;
}

int qcnt(int x){
	int ans=0;
	for(int i=x;i;i-=lbit(i)) ans+=c2[i];
	return ans;
}

signed main(){
	Ios;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i].pos;
	for(int i=1;i<=n;i++) cin>>a[i].v, ve.pb(a[i].v);
	
	sort(ve.begin(), ve.end());
	ve.erase(unique(ve.begin(), ve.end()), ve.end());
	
	sort(a+1, a+n+1, cmp);
	
	for(int i=1;i<=n;i++) a[i].v = get(a[i].v);
	
	int ans = 0;
	for(int i=1;i<=n;i++)
	{
		int pos = a[i].pos, v = a[i].v;
		int sum = qsum(v), cnt = qcnt(v);
		ans += cnt*pos - sum;
		
		add(v, pos);
	}
	cout << ans;
	
	return 0;
}

以后遇到这样的题目会越来越熟练的,加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值