F. Points (线段树)

题目

https://codeforces.com/contest/1701/problem/F
在这里插入图片描述
样例

input
7 5
8 5 3 2 1 5 6
output
0
0
1
2
5
1
5

思路

这里有官方题解
https://codeforces.com/blog/entry/104671

  • 考虑每个存在的点 i ,令在它右边d范围,即 [i+1, i+d] 范围内的点的个数为 f(i)那,那么当 i 为左端点时,右边两个点就是这f(i)个点里面随便选两个。每个时刻的答案就是所有的 f(i)*(f(i)-1)/2 之和。
  • f(i)*(f(i)-1)/2 = (f(i)^2 - f(i)) /2
  • 用线段树维护 f(i)^0, f(i)^1, f(i)^2。其中 f(i)^0 代表存在的点的个数。
  • 每加/减一个点,那么它左边d个数的 f(i) 均加/减 1,f(i)^2 相应可以用 f(i)f(i)^0 更新出来。新加的那个点的 f(i) 值就为它右边d个位置的点的个数,即 f(i)^0 的区间和。

在这里插入图片描述

时间复杂度,每个询问都是几个查询修改,所以是 O(q*Log)。
线段树最大线段长度是 a[i] 的最大值,即 200000。

代码

#include <bits/stdc++.h>
using namespace std;

template<typename t_val>
struct trip {
    t_val v0, v1, v2;
    trip(): v0(0), v1(0), v2(0) {}
    void operator+= (const trip<t_val> x) {
        v0 += x.v0;
        v1 += x.v1;
        v2 += x.v2;
    }
};

template<typename t_val, int maxn>
struct segment_tree {
#define LX(x) ((x)<<1)
#define RX(x) (((x)<<1)|1)
    struct node {
        int l, r;
        t_val val_0;
        t_val val_1;
        t_val val_2;
        t_val tag;
    };
    node a[maxn*5+10];

    void down_tag(int x) {
        if (!a[x].tag) return;
        const int lx=LX(x), rx=RX(x);

        a[lx].val_2 += a[lx].val_1 * 2*a[x].tag;
        a[lx].val_2 += a[lx].val_0 * a[x].tag*a[x].tag;
        a[lx].val_1 += a[lx].val_0 * a[x].tag;
        a[LX(x)].tag+=a[x].tag;

        a[rx].val_2 += a[rx].val_1 * 2*a[x].tag;
        a[rx].val_2 += a[rx].val_0 * a[x].tag*a[x].tag;
        a[rx].val_1 += a[rx].val_0 * a[x].tag;
        a[RX(x)].tag+=a[x].tag;

        a[x].tag=0;
    }

    void merge(int x) {
        const int lx=LX(x), rx=RX(x);
        a[x].val_0 = a[lx].val_0 + a[rx].val_0;
        a[x].val_1 = a[lx].val_1 + a[rx].val_1;
        a[x].val_2 = a[lx].val_2 + a[rx].val_2;
    }

    void build(int x, int l, int r) {
        a[x].l=l, a[x].r=r;
        if (l==r) {
            a[x].val_0 = 0;
            a[x].val_1 = 0;
            a[x].val_2 = 0;
            a[x].tag = 0;
            return;
        }
        build(LX(x), l, (l+r)>>1);
        build(RX(x), ((l+r)>>1)+1, r);
        merge(x);
    }

    void modify_conv0(int x, int pos, t_val val) {
        if (a[x].l == a[x].r) {
            if (a[x].val_0) {   // del
                a[x].val_0 = 0;
                a[x].val_1 = 0;
                a[x].val_2 = 0;
                a[x].tag = 0;
            }
            else {              // add
                a[x].val_0 = 1;
                a[x].val_1 = val;
                a[x].val_2 = val*val;
                a[x].tag = 0;
            }
            return;
        }

        down_tag(x);

        const int lx=x<<1, rx=(x<<1)|1;
        if (a[lx].r >= pos)
            modify_conv0(lx, pos, val);
        else
            modify_conv0(rx, pos, val);
        
        merge(x);
    }

    void modify_adds(int x, int L, int R, t_val val) {
        if (L <= a[x].l && a[x].r <= R) {
            a[x].val_2 += a[x].val_1 * 2*val;
            a[x].val_2 += a[x].val_0 * val*val;
            a[x].val_1 += a[x].val_0 * val;
            a[x].tag += val;
            return;
        }

        down_tag(x);

        const int lx=x<<1, rx=(x<<1)|1;
        if (a[lx].r >= L)
            modify_adds(lx, L, R, val);
        if (a[rx].l <= R)
            modify_adds(rx, L, R, val);
        
        merge(x);
    }

    trip<t_val> query(int x, int L, int R) {
        trip<t_val> tmpl, tmpr, ret;
        if (L <= a[x].l && a[x].r <= R) {
            ret.v0 = a[x].val_0;
            ret.v1 = a[x].val_1;
            ret.v2 = a[x].val_2;
            return ret;
        }

        down_tag(x);

        const int lx=x<<1, rx=(x<<1)|1;
        if (a[lx].r >= L)
            tmpl = query(lx, L, R);
        if (a[rx].l <= R)
            tmpr = query(rx, L, R);
        
        ret += tmpl;
        ret += tmpr;
        return ret;
    }

#undef LX
#undef RX
};


const int MAXN=200000;
segment_tree<long long, MAXN> TT;

void solve()
{
    TT.build(1, 1, MAXN);
    vector<int> used(MAXN+5);

    int q, d;
    
    long long ans;
    scanf("%d%d", &q, &d);
    for (int i=0, aa; i<q; i++) {
        scanf("%d", &aa);
        trip<long long> tmp_trip;
        if (aa<MAXN) tmp_trip = TT.query(1, aa+1, min(aa+d, MAXN));
        TT.modify_conv0(1, aa, tmp_trip.v0);
        if (aa>1) TT.modify_adds(1, max(aa-d, 1), aa-1, used[aa] ? -1 : 1);
        used[aa]^=1;

        tmp_trip = TT.query(1, 1, MAXN);
        ans = (tmp_trip.v2-tmp_trip.v1)/2;
        printf("%lld\n", ans);
    }
}


int main()
{
    solve();
    return 0;
}

/*

7 5
8 5 3 2 1 5 6

0
0
1
2
5
1
5


*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值