题意:

思路:
计数问题,考虑贡献即可
对于每一个V形端点,维护这个端点两端大于它的数的个数,那么这个点的贡献就是左边比它大的数*右边比它大的数,用树状数组维护即可
这是树状数组的应用之一,用于维护左右两边比它大or小的数的个数
对于维护左边的个数,我们可以从左到右扫描一遍,然后用树状数组去维护权值序列,权值序列记录的是值域,在扫描之前,权值序列所有数为0,每添加一个数就单点修改,同时树状数组也维护了前缀和的变化,然后查询就是查一下权值序列的前缀和就好了
Code:
- #include
- #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<
" "<'\n'; - }
因为树状数组的这种功能,它也能维护序列的逆序对个数:
- #include
- #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<
'\n'; - return 0;
- }
总结:
树状数组的应用:用于维护左右两边比它大or小的数的个数,也可维护逆序对个数