离散化 + 树状数组

先看一下一般求逆序数的思路:设置vis数组,表示数字在前面遍历过程中出现的次数;遍历数组,vis[ar[i]]加一,然后求出大于ar[i]的数在前面出现的次数并加入到ans中。
当序列数字过大是可以采用离散化处理,因为只需要知道大小关系即可,不必知道到底大了多少;对于求大于ar[i]的数出现的次数,可以采用树状数组求和:sum(vis[maxx]-vis[ar[i]])。

再回到本题,要求出所有区间的逆序数的总和,换一句话说就是求出所有逆序对所在区间数的总和。
当(i,j)为逆序对时,其对ans的贡献为i* (n-j+1)(考虑到树状数组,下标从1开始)。
当(i_1,j)(i_2,j)(i_3,j)...为逆序对时,其对ans的贡献为:i_1* (n-j+1)+i_2* (n-j+1)+i_3* (n-j+1)+...=sum(i)* (n-j+1)。其余步骤回到求逆序数的思路即可。

还有个小问题,最后输出的值会爆long long(活久见。。。),可用__int128解决。

代码:

#include <iostream>
#include <queue>
#include <set>
#include <map>
#include <vector>
#include <stack>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <cctype>
#include <functional>
#include <string>
#include <cstring>
#include <sstream>
#include <deque>
#define fir first
#define sec second
using namespace std;

typedef __int128 ll;
typedef pair<int,int> P;
typedef pair<P,int> Q;

const int inf1=2e9+9;
const ll inf2=8e18+9;
const ll mol=1e9+7;
const int maxn=1e6+9;
const ll maxx=1e12+9;

int n,ar[maxn];
P tmp[maxn];
ll d[maxn];

void Print(ll x)
{
    if(!x) return;
    Print(x/10);
    printf("%d",x%10);
}
ll sum(int p)
{
    ll res=0;
    while(p) res+=d[p],p-=(p&(-p));
    return res;
}
void add(int p,int t)
{
    while(p<=n) d[p]+=t,p+=(p&(-p));
}
void init()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&ar[i]),ar[i]++,tmp[i].fir=ar[i],tmp[i].sec=i;
    sort(tmp+1,tmp+n+1);
    int cnt=2;
    ar[tmp[1].sec]=1;
    for(int i=2;i<=n;i++)
    {
        if(tmp[i].fir==tmp[i-1].fir) ar[tmp[i].sec]=ar[tmp[i-1].sec];
        else ar[tmp[i].sec]=cnt++;
    }
}
int main()
{
    init();
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        ll t=n-i+1;
        ans+=(sum(n)-sum(ar[i]))*t;
        add(ar[i],i);
    }
    if(ans==0) printf("0");
    else Print(ans);
}