2020牛客寒假算法基础训练营day04 H-坐火车

2020牛客寒假算法基础训练营day04 H-坐火车

思路

用树状数组维护\([L,R]\)区间的答案,每次回答完后\(O(logn)\)更新。

具体怎么做呢?

我们假设说目前考虑颜色\(col\),我左边有\(x\)\(col\),右边有\(num-x\)\(col\)

那么\(col\)给答案的贡献就是\(x*(num-x)\)

假设我当前位置的颜色是\(c\),那么我考虑完我这个位置的时候,我往右边走一步,那么左边多一个\(c\),右边少一个\(c\)

所以我只需要用树状数组维护左右每个颜色的乘积,每次修改两次(减去上一次的答案,添加新的答案)。

但是维护乘法爆\(long\ long\)了,我不知道为啥,我觉得按道理来说\(5e5*5e5*5e5=1e17\)不会炸。

如果有明白的朋友请联系我。

接下来我们看看怎么维护这样一个乘积。

假设说我在位置\(i\),颜色\(c\)左边有\(x\)个,右边有\(sum-x\)个。

此时的答案就是\(x*(sum-x)=x*sum-x^2\)

继续遍历,到达位置\(j\),左边的颜色\(c\)\(x+1\)个,右边有\(sum-x-1\)个。

此时的答案就是\((x+1)*(sum-x-1)=x*sum-x^2-x+sum-x-1\)

所以我们只需要减去上次的\(x\),在加上一个\(sum-x-1\)就可以用加法代替乘法更新答案了。

#include
using namespace std;
typedef long long ll;
const int maxn = 5e5+10;
int n, col[maxn], r[maxn], l[maxn];

inline int lowbit(int x){return x&(-x);}

int mxx;
ll c[maxn];
inline void add(int x, ll y){
    for(; x <= mxx; x += lowbit(x)) c[x] += y;
}

ll ask(int x){
    ll ans = 0;
    for(; x; x -= lowbit(x)) ans += c[x];
    return ans;
}

ll vis[maxn];
ll used[maxn];

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d%d%d", &col[i], &l[i], &r[i]);
        mxx = max(mxx, max(col[i], max(l[i], r[i])));
    }
    for(int i = 1; i <= n; i++) vis[col[i]]++;
    ll ans = 0;
    for(int i = 1; i <= n; i++)
    {
        int x = col[i];
        add(x, -used[x]);
        ans = ask(r[i]) - ask(l[i]-1);
        used[x]++;
        add(x, vis[x]-used[x]);
        printf("%lld ", ans);
    }

    return 0;
}

你可能感兴趣的:(2020牛客寒假算法基础训练营day04 H-坐火车)