2019吉林省赛 Strange Hobby 动态开点线段树

Strange Hobby

题意:有一个长度为n序列,有q次操作,每次操作要么修改序列某个元素要么查询区间内有多少子区间x的个数为奇数个。
解法:我们对每个数字建立动态开点01线段树,线段树维护区间内区间和为奇数的子区间个数,然后就是一个水题了…我们对每个区间维护这5个信息:val,len,sumL,sumR,sum,分别表示区间和为奇数的的子区间个数,区间长度,包含左端点且区间和为奇数的区间个数,包含右端点且区间和为奇数的个数,区间和,然后我们就只有一个问题:区间合并,我们用ls,rs表示左儿子,右儿子,很显然 v a l + = v a l [ l s ] + v a l [ r s ] val+=val[ls]+val[rs] ,那么我们还需要计算左儿子区间右端点和右儿子区间左端点拼接的合法区间,左儿子区间和奇数+右儿子区间和偶数: v a l + = s u m R [ l s ] ( l e n [ r s ] s u m L [ r s ] ) val+=sumR[ls]*(len[rs]-sumL[rs]) ,左儿子区间和偶数+右儿子区间和奇数: v a l + = ( l e n [ l s ] s u m R [ l s ] ) s u m L [ r s ] val+=(len[ls] - sumR[ls])*sumL[rs] ,sumL,sumR的合并亦同理,不多说了
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 4e5 + 10;
map<int, int> mp;
int rt[maxn], ls[maxn * 20], rs[maxn * 20], a[maxn], cnt, cnt2;
struct node {
    ll val;
    int len, sumL, sumR, sum;
    node operator+(const node &t) const {
        node tmp;
        tmp.val = val + t.val;
        tmp.val += 1ll * sumR * (t.len - t.sumL);
        tmp.val += 1ll * (len - sumR) * t.sumL;
        tmp.len = len + t.len;
        tmp.sum = sum + t.sum;
        if (sum % 2)
            tmp.sumL = sumL + t.len - t.sumL;
        else
            tmp.sumL = sumL + t.sumL;
        if (t.sum % 2)
            tmp.sumR = t.sumR + len - sumR;
        else
            tmp.sumR = t.sumR + sumR;
        return tmp;
    }
} tree[maxn * 20];
void init(int &o, int len) {
    o = ++cnt;
    tree[o].len = len;
}
void up(int &o, int l, int r, int k, int v) {
    if (!o)
        o = ++cnt;
    if (l == r) {
        tree[o].val = tree[o].sumL = tree[o].sumR = tree[o].sum = v;
        tree[o].len = 1;
        return;
    }
    int m = (l + r) / 2;
    if (k <= m)
        up(ls[o], l ,m, k, v);
    else
        up(rs[o], m + 1, r, k, v);
    if (!ls[o])//细节,如果空节点也需要填上区间长度,以防出错
        init(ls[o], m - l + 1);
    if (!rs[o])
        init(rs[o], r - m);
    tree[o] = tree[ls[o]] + tree[rs[o]];
}
node qu(int& o, int l, int r, int ql, int qr) {
    if (!o)
        init(o, r - l + 1); //同上的细节
    if (l >= ql && r <= qr)
        return tree[o];
    int m = (l + r) / 2;
    if (qr <= m)
        return qu(ls[o], l, m, ql, qr);
    else if (ql > m)
        return qu(rs[o], m + 1, r, ql, qr);
    return qu(ls[o], l, m, ql, qr) + qu(rs[o], m + 1, r, ql, qr);
}
int main() {
    int n, x, q, opt, v, l, r;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        if (!mp.count(a[i]))
            mp[a[i]] = ++cnt2;
        up(rt[mp[a[i]]], 1, n, i, 1);
    }
    scanf("%d", &q);
    while (q--) {
        scanf("%d", &opt);
        if (opt == 1) {
            scanf("%d%d", &x, &v);
            up(rt[mp[a[x]]], 1, n, x, 0);
            a[x] = v;
            if (!mp[a[x]])
                mp[a[x]] = ++cnt2;
            up(rt[mp[a[x]]], 1, n, x, 1);
        }
        else {
            scanf("%d%d%d", &l, &r, &x);
            printf("%lld\n", qu(rt[mp[x]], 1, n, l, r).val);
        }
    }
}
发布了302 篇原创文章 · 获赞 98 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/ccsu_cat/article/details/100565077