链接:
题目描述
珂朵莉给了你一个序列,有
个子区间,求出她们各自的逆序对个数,然后加起来输出
输入描述:
第一行一个数 n 表示这个序列 a 的长度
之后一行 n 个数,第i个数表示ai
输出描述:
输出一行一个数表示答案
示例1
输入
10 1 10 8 5 6 2 3 9 4 7
输出
270
题解:假如a[i]和a[j]是一组逆序对,则包含它们的子区间个数为i * (n - j + 1)。使用树状数组求解,先离散化,再每次加入当前数的位置,这样每次询问求的就是位置和,即如果a1,a3,a5都与a7成逆序对,那么询问sum(n) - sum(a[7])的话可以直接求得1 + 3 + 5的值。
#include <bits/stdc++.h>
using namespace std;
const long long mod = 1e18;
const int maxn = 1e6 + 10;
int n;
int a[maxn], b[maxn];
long long bit[maxn];
long long sum(int i)
{
long long res = 0;
while(i > 0){
res += bit[i];
i -= i & -i;
}
return res;
}
void add(int i, int x)
{
while(i <= n){
bit[i] += x;
i += i & -i;
}
}
int main()
{
scanf("%d", &n);
int cnt = 0;
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
b[cnt++] = a[i];
}
//离散化
sort(b, b + cnt);
cnt = unique(b, b + cnt) - b;
for(int i = 1; i <= n; i++) a[i] = lower_bound(b, b + cnt, a[i]) - b + 1;
long long ans = 0, lans = 0;
for(int i = 1; i <= n; i++){
ans += (sum(n) - sum(a[i])) * (n - i + 1); //求(西格玛i)*(n-i+1);
if(ans >= mod) lans += ans / mod; ans %= mod; //爆long long 的计数技巧
add(a[i], i);
}
if(lans) printf("%lld%018lld\n", lans, ans); //爆long long 的计数技巧
else printf("%lld\n", ans);
return 0;
}