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,−108≤a[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 1≤a[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;
}
以后遇到这样的题目会越来越熟练的,加油!