【树状数组】楼兰图腾

241. 楼兰图腾 - AcWing题库

题意:

思路:

计数问题,考虑贡献即可

对于每一个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小的数的个数,也可维护逆序对个数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值