USACO20OPEN Haircut G

题目链接

Solution

不会做,于是参考题解

假设现在 (mathrm{John}) 不再需要为肆意生长的头发而烦恼了,而是成为了一个因秃头而抓狂的脱发人士。现在,我们把理发看成是生发,那么第 (i) 根头发的极限长度就是 (a_i).

头发从 (0) 开始生长,在到达极限长度之前,每秒生长 (1) 个单位长度。现在我们把题目中的 (j) 看成是时间,那么第 (j) 秒头发最长一定不会超过 (j). 这不是正好符合题目限制吗?

显而易见的,第 (0) 秒的逆序对个数是 (0). 那么,我们现在需要考虑的是第 (j)新增的逆序对。如果第 (i) 根头发的最大高度 (a_i = j - 1),说明第 (j) 秒它已经停止生长了。当 (j-1) 秒时,那些跟它比肩的小伙伴(指还未停止生长的头发)很可能借助从这一秒开始超过它。

也就是说,当满足 (j < i)(a_j > a_i) 时,从第 (a_i + 1)开始(i)(j) 为答案贡献逆序对。观察前半句话,这还是个求逆序对,可以用树状数组维护;对于后半句话,就涉及区间加法了。因为这题不需要多次询问,因此直接差分即可。总时间复杂度 (mathrm{O(nlog_n)}).

Code

#include <iostream>
#include <cstdio>
#include <cstring>
#define LL long long
#define lowbit(x) (x & (-x))
using namespace std;

const int N = 2333333;
LL n, a[N], sum[N], t[N];

void add(LL x) { while(x <= n + 1) t[x]++, x += lowbit(x); }
LL query(LL x)
{
    LL res = 0;
    while(x) res += t[x], x -= lowbit(x);
    return res;
}

int main()
{
    scanf("%lld", &n);
    memset(t, 0, sizeof(t));
    memset(sum, 0, sizeof(sum));
    for(int i = 1; i <= n; i++)
    {
        scanf("%lld", &a[i]), a[i]++;
        sum[a[i] + 1] += query(n + 1) - query(a[i]);
        add(a[i]);
    }
    for(int i = 1; i <= n; i++)
    {
        sum[i] += sum[i - 1];
        printf("%lld
", sum[i]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Andy-park/p/14069680.html