【笔记】整体二分/并行二分,CDQ 分治

整体二分/并行二分

口胡一下理解
例题 [POI2011]MET-Meteors
对于 m 种颜色,每个颜色的答案就是一个属于 1~Q 的数字
我们想象对于 1~Q 开一棵线段树一样的东西,最开始所有颜色都在根节点,表示这些颜色的答案所在区间一开始是 1~Q
每次模拟一遍 1~Q 所有操作,顺便将所有颜色分类到下一层;也就是在遇到某个节点的mid时判断一下这个节点内的颜色的去向
总的复杂度是nlogn*logn

#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 300005;
const long long inf = 10000000000ll;
typedef long long ll;
int N, M, K, col[MAXN], des[MAXN];
struct operate {
    int l, r, a;
} Q[MAXN];
struct segmentTree {
    #define lson (x<<1)
    #define rson (x<<1|1)
    ll sum[MAXN<<2];
    void clear() { memset(sum, 0, sizeof(sum)); }
    void update(int x, int l, int r, int _l, int _r, int k) {
        if (l>=_l && r<=_r) sum[x] += k, sum[x] = sum[x]> inf ? inf : sum[x];
        else {
            int mid = (l + r) >> 1;
            if (mid>=_l) update(lson, l, mid, _l, _r, k);
            if (mid< _r) update(rson, mid+1, r, _l, _r, k);
        }
    }
    ll query(int x, int l, int r, int p) {
        if (l==r) return sum[x];
        else {
            int mid = (l + r) >> 1;
            if (mid>=p) return sum[x] + query(lson, l, mid, p);
            else return sum[x] + query(rson, mid+1, r, p);
        }
    }
    void print(int x, int l, int r) {
        printf("[%d], [%d, %d], sum = %lld\n", x, l, r, sum[x]);
        if (l< r) {
            int mid = (l + r) >> 1;
            print(lson, l, mid), print(rson, mid+1, r);
        }
    }
} ST;
struct node {
    node(int _id, int _l=0, int _r=0) {
        id = _id, l = _l, r = _r, m = (l + r) >> 1;
    }
    int id, l, r, m;
};
vector<int> spa[MAXN];
vector<int> vec[MAXN<<2];
queue<node> que[2];
int ansn[MAXN];
void cal(int x, int l, int r)
{
    if (l==r) {
        for (auto c:vec[x]) ansn[c] = l;
    } else {
        int mid = (l + r) >> 1;
        cal(x<<1, l, mid), cal(x<<1|1, mid+1, r);
    }
}
int main()
{
    scanf("%d%d", &N, &M);
    for (int i=1; i<=M; i++) scanf("%d", &col[i]), spa[col[i]].push_back(i);
    for (int i=1; i<=N; i++) scanf("%d", &des[i]);
    scanf("%d", &K);
    for (int i=1; i<=K; i++) scanf("%d%d%d", &Q[i].l, &Q[i].r, &Q[i].a);

    for (int i=1; i<=N; i++) vec[1].push_back(i);
    que[0].push(node(1, 1, K));
    int cur = 0;
    for (;; cur^=1) {
        //printf("cur = %d\n", cur);
        ST.clear();
        for (int i=1; i<=K; i++) {
            //printf("    i=%d\n", i);
            if (Q[i].l<=Q[i].r) ST.update(1, 1, M, Q[i].l, Q[i].r, Q[i].a);
            else ST.update(1, 1, M, 1, Q[i].r, Q[i].a), ST.update(1, 1, M, Q[i].l, M, Q[i].a);
            //ST.print(1, 1, M);
            //for (int o=1; o<=M; o++) printf("%lld ", ST.query(1, 1, M, o)); printf("\n");
            if (!que[cur].empty() && i==que[cur].front().m) {
                int x = que[cur].front().id;
                int l = que[cur].front().l, r = que[cur].front().r, m = que[cur].front().m;
                //printf("    [%d, %d]\n", l, r);
                que[cur].pop();
                for (auto c:vec[x]) {
                    //printf("        col = %d, ", c);
                    ll s = 0;
                    for (auto p:spa[c]) s += ST.query(1, 1, M, p);
                    //printf(" sum = %lld, ", s);
                    if (s>=des[c]) vec[x<<1].push_back(c);//, printf(" -> lson\n");
                    else vec[x<<1|1].push_back(c);//, printf(" -> rson\n");
                }
                if (l< m) que[cur^1].push(node(x<<1, l, m));
                if (m+1< r) que[cur^1].push(node(x<<1|1, m+1, r));
            }
        }
        if (que[cur^1].empty()) break;
    }
    cal(1, 1, K);
    for (int i=1; i<=N; i++) {
        ll s = 0;
        for (auto p:spa[i]) s += ST.query(1, 1, M, p);
        if (s< des[i]) ansn[i] = 0;
    }
    for (int i=1; i<=N; i++) if (ansn[i]) printf("%d\n", ansn[i]); else puts("NIE");
}

CDQ 分治

解决多维偏序问题,用一个 log 干掉一维
以二维偏序为例,假设每个点可以转移到两维都比它大的点
考虑对其中一维二分,变成两个子问题,先递归做小的部分,然后将全部小的转移到全部大的(比如对每个大的,去查询对应小的最值,可以双指针啥的),然后递归做大的部分

posted @ 2022-03-25 20:56  zrkc  阅读(66)  评论(0编辑  收藏  举报