2020牛客寒假算法基础集训营4 H- 坐火车 (桶,树状数组)

链接:https://ac.nowcoder.com/acm/contest/3005/H
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

牛牛是一名喜欢旅游的同学,在来到渡渡鸟王国时,坐上了颜色多样的火车。

牛牛同学在车上,车上有 n 个车厢,每一个车厢有一种颜色。

他想知道对于每一个正整数 x∈[1, n]x \in [1,\ n]x∈[1, n] ,集合 {(i, x, j) ∣ i<x<j, lx≤coli=colj≤rx}{ (i,\ x,\ j)\ |\ i < x < j,\ l_x \le col_i = col_j \le r_x }{(i, x, j) ∣ i<x<j, lx≤coli=colj≤rx} 中包含多少个元素。

换句话说,就是要求每一个车厢两边有多少对颜色相同的车厢,并且这一对车厢的颜色要在 lx 到 rx 之间。其中 coli 代表 i 号车厢的颜色,lx,rx 代表颜色的限制。

输入描述:

第一行一个正整数n。
第二行 n 个三元组,每个三元组包括三个正整数 (coli, li, ri),输入中没有括号,这 3n 个正整数之间均只用空格隔开,详见样例。

输出描述:

输出一行 n 个非负整数代表答案。

示例1

输入

[复制](javascript:void(0)😉

5
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

输出

[复制](javascript:void(0)😉

0 3 4 3 0

备注:

1≤n≤5⋅1051 \le n \le 5 \cdot 10^51≤n≤5⋅105
1≤coli, li, ri≤5⋅1051 \le col_i,\ l_i,\ r_i \le 5 \cdot 10^51≤coli, li, ri≤5⋅105

思路:

我们从左到右,每一个位置i,用树状数组维护每种颜色的该车厢左右的对数。

因为树状数组有维护动态前缀和的功能,ask(r) 代表0~r颜色中,有多少相同颜色的车厢左右的对数。

所以每一个位置i的答案是 \(ask(r)-ask(l-1)\)

再来看如何用树状数组维护每种颜色的该车厢左右的对数

我们用数组\(cnt2[j]\) 代表第i个位置之后有多少个j颜色。

用数组\(cnt1[j]\) 代表第i个位置之前有多少个j颜色。

我们知道 i=1 的位置答案肯定为0。

思考如何向右转移:

从第i-1个位置移动到了第i位置时,如果i-1左边有颜色为\(col[i]\) 的,应该在颜色为\(col[i]\)的车相对中减去\(cnt1[a[i]]\)

接着\(cnt2[col[i]]\)减去1,\(cnt1[col[i - 1]]\) 加上1.

有因为原本的i-1位置变成了i位置左边的颜色,所以应该在颜色为\(col[i-1]\)的车相对中减去\(cnt2[col[i]]\)个.

对cnt1和cnt2的操作直接对数组修改即可,对颜色组成的车相对数用树状数组单点更新即可。

即从第i个位置的答案 \(O(logn)\) 时间复杂度内转移到第i+1位置,同时得到第i+1位置的答案,

这样总体时间复杂度就是\(O(n*logn)\)

代码:

int l[maxn];
int r[maxn];
int a[maxn];
int cnt1[maxn];
int cnt2[maxn];
ll tree[maxn];
int n;
int lowbit(int x)
{
    return x & (-x);
}
void add(int x, ll val)
{
    while (x < maxn)
    {
        tree[x] += val;
        x += lowbit(x);
    }
}
ll ask(int x)
{
    ll res = 0ll;
    while (x)
    {
        res += tree[x];
        x -= lowbit(x);
    }
    return res;
}
ll ans[maxn];
 
int main()
{
    //freopen("D:\\code\\text\\input.txt","r",stdin);
    //freopen("D:\\code\\text\\output.txt","w",stdout);
    n = readint();
    repd(i, 1, n)
    {
        a[i] = readint();
        l[i] = readint();
        r[i] = readint();
    }
    ll num = 0ll;
    repd(i, 2, n)
    {
        cnt2[a[i]]++;
    }
    repd(i, 2, n - 1)
    {
        if (cnt1[a[i]])
            add(a[i], -cnt1[a[i]]);
        cnt2[a[i]]--;
        cnt1[a[i - 1]]++;
        if (cnt2[a[i - 1]])
            add(a[i - 1], cnt2[a[i - 1]]);
        ans[i] = ask(r[i]) - ask(l[i] - 1);
    }
    repd(i, 1, n)
    {
        printf("%lld%c", ans[i], i == n ? '\n' : ' ');
    }
 
    return 0;
}