题目
思路
本质上这个问题就是在求分别在一个数左边和右边的,大于该数的个数的乘积(小于同理)
维护一个下标指元素大小的线段树来方便求大于和小于某数值的元素个数
通过从左到右遍历,来确定此时的线段树状态一定不包括右边
因为必须一边更新状态,一边求取区间和,所以不能用前缀和,必须用树状数组
最后通过左big和右big值的总和是一定的这个性质来优化,减少一次反向遍历
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5+10;
int y[N];
int t[N];
LL big, small;
LL res1, res2;
int n;
int lowbit(int x)
{
return x & -x;
}
void update(int x, int d)
{
for(; x <= n; x += lowbit(x)) t[x] += d;
}
int query(int x)
{
int retval = 0;
for(; x >= 1; x -= lowbit(x)) retval += t[x];
return retval;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> y[i];
update(y[i], 1);
big = query(n) - query(y[i]);
res1 += big * (n-y[i]-big);
small = query(y[i] - 1);
res2 += small * (y[i]-1-small);
}
cout << res1 << " " << res2;
return 0;
}