HDU6183 cdq分治+线段树

简要题意:
1. 每次给 (x,y) 染上颜色 c
2. 询问x[1,X]y[y1,y2]这个矩形内部的颜色种类。

对于一个修改能够影响的询问,是在这个修改之后的所有 x 坐标大于当前修改位置的询问。
每次询问需要回答的就是x在小于当前位置的,且时间早于当前位置的那些修改中, [y1,y2] 部分的答案。这其实就是一个三维的偏序关系。
现在问题已经简化成了,每次给一点染若干次色,问区间内有多少种颜色。
颜色数目只有 50 个, 考虑压位线段树来快速查询。
因为时间本身是有序的,这样就保持了第一维有序。然后我们用cdq分治处理 x 坐标这一维,用线段树维护y这一维。复杂度 O(nlognlogn)

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

const LL maxn = 160000;

LL t;

struct ASK {
    LL x, y, y1, y2, c, t, type;
    bool operator < (const ASK & b)const {
        return x < b.x;
    }
};

vector<ASK> _ask(maxn);
ASK askl[maxn],  askr[maxn];
LL ans[maxn];

struct Seg{
    LL val;
} tr[4100000];

void pushup(LL rt) {
    tr[rt].val = tr[rt<<1].val | tr[rt<<1|1].val;
}

void update(LL p, LL c, LL l, LL r, LL rt, LL tag) {
    if(l == r) {
        if(tag)
            tr[rt].val |= (1LL << c);
        else
            tr[rt].val = 0;
        return ;
    }
    LL m = (l + r) >> 1;
    if(p <= m) update(p, c, l, m, rt<<1, tag);
    else update(p, c, m+1, r, rt<<1|1, tag);
    pushup(rt);
}

LL ask(LL L, LL R, LL l, LL r, LL rt) {
    if(L <= l && R >= r)
        return tr[rt].val;
    LL m = (l + r) >> 1;
    LL res = 0;
    if(L <= m) res |= ask(L, R, l, m, rt<<1);
    if(R > m) res |= ask(L, R, m+1, r, rt<<1|1);
    pushup(rt);
    return res;
}

LL bitcount(LL x) {
    return x == 0?0:x%2+bitcount(x/2);
}

void cdq(LL l, LL r) {
    if(l == r) return ;
    LL m = l + r >> 1;
    cdq(l, m);
    LL cl = 0, cr = 0;
    for(LL i = l; i <= m; i++)
        askl[cl++] = _ask[i];
    for(LL i = m+1; i <= r; i++)
        askr[cr++] = _ask[i];
    sort(askl, askl+cl);
    sort(askr, askr+cr);
    LL i = 0;
    for(LL j = 0; j < cr; j++) {
        if(askr[j].type == 2) {
            while(askr[j].x >= askl[i].x && i < cl) {
                if(askl[i].type == 1)
                    update(askl[i].y, askl[i].c, 1, 1000000, 1, 1);
                i++;
            }
            ans[askr[j].t] |= ask(askr[j].y1, askr[j].y2, 1, 1000000, 1);
        }
    }
    for(LL _ = 0; _ < i; _++)
        if(askl[_].type == 1)
            update(askl[_].y, askl[_].c, 1, 1000000, 1, 0);
    cdq(m+1, r);
}


int main() {
    LL o;
    scanf("%lld", &o);
    for(;;) {
        if(o == 3) break;
        t = 0;
        for(;;) {
            scanf("%lld", &o);
            if(o == 1) {
                scanf("%lld%lld%lld", &_ask[t].x, &_ask[t].y, &_ask[t].c);
                _ask[t].t = t;
                _ask[t].type = 1;
                t++;
            } else if(o == 2) {
                scanf("%lld%lld%lld", &_ask[t].x, &_ask[t].y1, &_ask[t].y2);
                _ask[t].t = t;
                _ask[t].type = 2;
                t++;
            } else break;
        }
        cdq(0, t-1);
        for(LL i = 0; i < t; i++) {
            if(_ask[i].type == 2)
                printf("%lld\n", bitcount(ans[i])), ans[i] = 0;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值