题意:
思路:
计数问题,考虑贡献即可
对于每一个V形端点,维护这个端点两端大于它的数的个数,那么这个点的贡献就是左边比它大的数*右边比它大的数,用树状数组维护即可
这是树状数组的应用之一,用于维护左右两边比它大or小的数的个数
对于维护左边的个数,我们可以从左到右扫描一遍,然后用树状数组去维护权值序列,权值序列记录的是值域,在扫描之前,权值序列所有数为0,每添加一个数就单点修改,同时树状数组也维护了前缀和的变化,然后查询就是查一下权值序列的前缀和就好了
Code:
#include <bits/stdc++.h>
#define low(x) (x&(-x))
#define int long long
using namespace std;
const int mxn=2e5+10;
int n;
int a[mxn],upper[mxn],lower[mxn],tree[mxn];
int sum(int x){
int res=0;
for(int i=x;i;i-=low(i)) res+=tree[i];
return res;
}
void add(int x){
for(int i=x;i<=n;i+=low(i)) tree[i]++;
}
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
lower[i]=sum(a[i]);
upper[i]=sum(n)-sum(a[i]);
add(a[i]);
}
memset(tree,0,sizeof(tree));
int ans1=0,ans2=0;
for(int i=n;i>=1;i--){
ans1+=upper[i]*(sum(n)-sum(a[i]));
ans2+=lower[i]*sum(a[i]);
add(a[i]);
}
cout<<ans1<<" "<<ans2<<'\n';
}
因为树状数组的这种功能,它也能维护序列的逆序对个数:
#include <bits/stdc++.h>
#define int long long
#define low(x) (x&(-x))
using namespace std;
const int mxn=5e5+10;
int n;
int a[mxn],tree[mxn];
void add(int x,int k){
for(int i=x;i<=n;i+=low(i)) tree[i]+=k;
}
int sum(int x){
int res=0;
for(int i=x;i;i-=low(i)) res+=tree[i];
return res;
}
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
memset(tree,0,sizeof(tree));
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int ans=0;
for(int i=1;i<=n;i++){
ans+=sum(n)-sum(a[i]);
add(a[i],1);
}
cout<<ans<<'\n';
return 0;
}
总结:
树状数组的应用:用于维护左右两边比它大or小的数的个数,也可维护逆序对个数