牛客练习赛7 E 题 珂朵莉的数列 【树状数组 + 思维】

传送门
//对于一个数n, 它有n*(n+1)/2个子区间, 问这些子区间的逆序对数之和.

//那么这个区间问题一般都是计算贡献问题, 所以先分析问题如果有 i < j 且 a[i] > a[j] 那么区间1<= l <=i, j <= r <= n, 就是包含了这个逆序对, 也就是有i*(n-j+1)个区间. 首先数字太大需要离散化, 然后我们更新的是每一个数的位置, add(pos, i); 并且从后往前扫, 为什么了? 也就是我们假设第4个位置的数a4和前面的三个数(a1, a2, a3)都是逆序对关系, 那么ans += (pos(a1) + pos(a2) + pos(a3)) * (n - 4 + 1) , 有多少个进行类推, 也就是包含(a1, a4)这个逆序对的是(1 - pos(a1))x(n-4+1), 所以后面进行类推, 所以要进行推位置进行, 然后询问位置和, 也就是求后缀来求位置和. (因为前缀处理不了, 不行可以试试~ 反正我试了下, 没写出来, 所以用后缀和的方法可以很好地求出ans和位置和~~~记住!!!)

注意一点的是: 最后的答案可以到达O(n^4)方级别, 所以long long 也是存不下的, 那么就有一个小技巧对于>1e18 && < 1e36的数我们就可以有两位来存, 相当于1e18进制, 那么最后输出的特判下就行啦~

AC Code

const int maxn = 1e6+5;
ll c[maxn], a[maxn];
vector<int>ve;
ll n;
void add(int x, int s){ for(;x<=n;x+=x&-x) c[x]+=s;}
ll getsum(int x){ ll res=0;for(;x;x-=x&-x) res+=c[x];return res;}
int getid(int x) {
    return lower_bound(ve.begin() , ve.end(), x) - ve.begin() + 1;
}
void solve()
{
    cin >> n;
    for (int i = 1 ; i <= n ; i ++) {
        cin >> a[i];
        ve.pb(a[i]);
    }
    sort(ve.begin(), ve.end());
    ve.erase(unique(ve.begin(),ve.end()), ve.end());
    ll ans1 = 0 , ans = 0;
    ll mm = 1e18;
    for (int i = 1 ; i <= n ; i ++) {
        int pos = getid(a[i]);
        ans += (getsum(n) - getsum(pos))*(n-i+1);  // 如前面分析的那样.
        if (ans > mm) {
            ans1 += ans/mm;
            ans %= mm;
        }
        add(pos, i);
    }
    if(ans1) printf("%lld%018lld\n", ans1, ans);  //爆ll的存储技巧~
    else printf("%lld\n", ans);

}

你可能感兴趣的:(树状数组)