AcWing - 254 - 天使玩偶 = CDQ分治

下面是一种正确的CDQ分治,使用线段树来实现,渐进复杂度到达要求。但是貌似CDQ分治在解决偏序问题的时候要尽可能使用树状数组。小心不要把i和j写反了。下面这个写法虽然没有WA,但是TLE了,这个非常麻烦。事实上是因为有很多多余的操作影响了常数,比如多余的排序、没有用的修改、询问。当然这个线段树也可以换成几倍快的树状数组。

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

const int INF = 1e9;
//将题目的[0,1e6]偏移到[1,1e6+1]
const int H = 1e6 + 1;

int st[H * 4 + 5];
inline void push_up(int o) {
    st[o] = min(st[o << 1], st[o << 1 | 1]);
}

void build(int o, int l, int r) {
    if(l == r) {
        st[o] = INF;
    } else {
        int mid = (l + r) >> 1;
        build(o << 1, l, mid);
        build(o << 1 | 1, mid + 1, r);
        push_up(o);
    }
}

void update(int o, int l, int r, int x, int v) {
    if(l == r) {
        st[o] = min(st[o], v);
    } else {
        int mid = (l + r) >> 1;
        if(x <= mid)
            update(o << 1, l, mid, x, v);
        else
            update(o << 1 | 1, mid + 1, r, x, v);
        push_up(o);
    }
}

void unupdate(int o, int l, int r, int x) {
    if(l == r) {
        st[o] = INF;
    } else {
        int mid = (l + r) >> 1;
        if(x <= mid)
            unupdate(o << 1, l, mid, x);
        else
            unupdate(o << 1 | 1, mid + 1, r, x);
        push_up(o);
    }
}

int query(int o, int l, int r, int ql, int qr) {
    if(ql <= l && r <= qr) {
        return st[o];
    } else {
        int mid = (l + r) >> 1, res = INF;
        if(ql <= mid)
            res = min(res, query(o << 1, l, mid, ql, qr));
        if(qr >= mid + 1)
            res = min(res, query(o << 1 | 1, mid + 1, r, ql, qr));
        return res;
    }
}

const int MAXNM = 1e6;

struct Query {
    int t, op, x, y, ans;
    bool operator<(const Query &q)const {
        return x < q.x;
    }
} q[MAXNM + 5], tq[MAXNM + 5];

inline int before_calc(int l, int r) {
    int tqtop = 0, mid = (l + r) >> 1;
    for(int i = l; i <= mid; ++i) {
        if(q[i].op == 1)
            tq[++tqtop] = q[i];
    }
    for(int i = mid + 1; i <= r; ++i) {
        if(q[i].op == 2)
            tq[++tqtop] = q[i];
    }
    sort(tq + 1, tq + 1 + tqtop);
    return tqtop;
}

inline void after_calc(int tqtop) {
    for(int i = 1; i <= tqtop; ++i) {
        if(tq[i].op == 1)
            unupdate(1, 1, H, tq[i].y);
        else
            q[tq[i].t].ans = tq[i].ans;
    }
}

int calc_upleft(int tqtop) {
    for(int i = 1, nxt; i <= tqtop; i = nxt) {
        for(nxt = i + 1; nxt <= tqtop && tq[i].x == tq[nxt].x; ++nxt);
        for(int j = i; j < nxt; ++j) {
            if(tq[j].op == 1)
                update(1, 1, H, tq[j].y, (-tq[j].x + tq[j].y));
        }
        for(int j = i; j < nxt; ++j) {
            if(tq[j].op == 2)
                tq[j].ans = min(tq[j].ans, (tq[j].x - tq[j].y) + query(1, 1, H, tq[j].y, H));
        }
    }
    after_calc(tqtop);
}

int calc_downleft(int tqtop) {
    for(int i = 1, nxt; i <= tqtop; i = nxt) {
        for(nxt = i + 1; nxt <= tqtop && tq[i].x == tq[nxt].x; ++nxt);
        for(int j = i; j < nxt; ++j) {
            if(tq[j].op == 1)
                update(1, 1, H, tq[j].y, (-tq[j].x - tq[j].y));
        }
        for(int j = i; j < nxt; ++j) {
            if(tq[j].op == 2)
                tq[j].ans = min(tq[j].ans, (tq[j].x + tq[j].y) + query(1, 1, H, 1, tq[j].y));
        }
    }
    after_calc(tqtop);
}

int calc_upright(int tqtop) {
    for(int i = tqtop, nxt; i >= 1; i = nxt) {
        for(nxt = i - 1; nxt >= 1 && tq[i].x == tq[nxt].x; --nxt);
        for(int j = i; j > nxt; --j) {
            if(tq[j].op == 1)
                update(1, 1, H, tq[j].y, (tq[j].x + tq[j].y));
        }
        for(int j = i; j > nxt; --j) {
            if(tq[j].op == 2)
                tq[j].ans = min(tq[j].ans, (-tq[j].x - tq[j].y) + query(1, 1, H, tq[j].y, H));
        }
    }
    after_calc(tqtop);
}

int calc_downright(int tqtop) {
    for(int i = tqtop, nxt; i >= 1; i = nxt) {
        for(nxt = i - 1; nxt >= 1 && tq[i].x == tq[nxt].x; --nxt);
        for(int j = i; j > nxt; --j) {
            if(tq[j].op == 1)
                update(1, 1, H, tq[j].y, (tq[j].x - tq[j].y));
        }
        for(int j = i; j > nxt; --j) {
            if(tq[j].op == 2)
                tq[j].ans = min(tq[j].ans, (-tq[j].x + tq[j].y) + query(1, 1, H, 1, tq[j].y));
        }
    }
    after_calc(tqtop);
}

void solve(int l, int r) {
    if(l == r)
        return;
    int mid = (l + r) >> 1;
    solve(l, mid);
    solve(mid + 1, r);

    //这里是没必要每一层都排序的,事实上是每一层直接归并就可以了
    int tqtop = before_calc(l, r);
    calc_upleft(tqtop);
    calc_downleft(tqtop);
    calc_upright(tqtop);
    calc_downright(tqtop);
    return;
}

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    int n, m;
    scanf("%d%d", &n, &m);
    int qtop = 0;
    for(int i = 1; i <= n; ++i) {
        int x, y;
        scanf("%d%d", &x, &y);
        ++qtop;
        q[qtop].op = 1;
        q[qtop].t = qtop;
        q[qtop].x = x + 1;
        q[qtop].y = y + 1;
        q[qtop].ans = INF;
    }
    for(int i = 1; i <= m; ++i) {
        int op, x, y;
        scanf("%d%d%d", &op, &x, &y);
        ++qtop;
        q[qtop].op = op;
        q[qtop].t = qtop;
        q[qtop].x = x + 1;
        q[qtop].y = y + 1;
        q[qtop].ans = INF;
    }
    build(1, 1, H);
    solve(1, qtop);
    for(int i = 1; i <= qtop; ++i) {
        if(q[i].op == 2) {
            printf("%d\n", q[i].ans);
        }
    }
}

加上快读,换成树状数组,树状数组修改时跳过不可能更新到当前询问的修改,换成归并排序,然后CDQ剪枝(某分支的操作类型全部是修改操作时,该分支没有任何询问会被更新,直接按x排序并返回该分支)。

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

namespace FastIO {
#define BUF_SIZE 1000000
    bool IOError = 0;
    inline char NextChar() {
        static char buf[BUF_SIZE], *pl = buf + BUF_SIZE, *pr = buf + BUF_SIZE;
        if(pl == pr) {
            pl = buf, pr = buf + fread(buf, 1, BUF_SIZE, stdin);
            if(pr == pl) {
                IOError = 1;
                return -1;
            }
        }
        return *pl++;
    }
#undef BUF_SIZE

    inline bool Blank(char c) {
        return c == ' ' || c == '\n' || c == '\r' || c == '\t';
    }

    template<class T> inline void Read(T &x) {
        char c;
        while(Blank(c = NextChar()));
        if(!IOError) {
            for(x = 0; '0' <= c && c <= '9'; c = NextChar())
                x = (x << 3) + (x << 1) + c - '0';
        }
    }

    template<class T> inline void Read2(T &x) {
        char c;
        bool f = 0;
        while(Blank(c = NextChar()));
        if(!IOError) {
            if(c == '-') {
                f = 1;
                c = NextChar();
            }
            for(x = 0; '0' <= c && c <= '9'; c = NextChar())
                x = (x << 3) + (x << 1) + c - '0';
        }
        if(f)
            x = -x;
    }

    template<class T> inline void PutChar(T x) {
        if(x > 9)
            PutChar(x / 10);
        putchar(x % 10 + '0');
    }

    template<class T> inline void Write(T &x) {
        PutChar(x);
        putchar('\n');
    }

    template<class T> inline void Write2(T &x) {
        if(x < 0) {
            putchar('-');
            PutChar(-x);
        } else
            PutChar(x);
        putchar('\n');
    }
}

using namespace FastIO;

const int INF = 1e9;
//将题目的[0,1e6]偏移到[1,1e6+1]
const int H = 1e6 + 1;

int bit[H + 5];

void build() {
    for(int i = 1; i <= H; ++i)
        bit[i] = INF;
}

void update(int x, int v) {
    for(; x <= H; x += x & -x)
        bit[x] = min(bit[x], v);
}

void unupdate(int x) {
    for(; x <= H; x += x & -x)
        bit[x] = INF;
}

int query(int x) {
    int res = INF;
    for(; x >= 1; x -= x & -x)
        res = min(res, bit[x]);
    return res;
}

const int MAXNM = 1e6;

struct Query {
    int t, op, x, y, ans;
    bool operator<(const Query &q)const {
        return x < q.x;
    }
} q[MAXNM + 5], tq[MAXNM + 5], tq2[MAXNM + 5];

bool cmp(const Query &q1, const Query &q2) {
    return q1.t < q2.t;
}

inline int before_calc(int l, int r) {
    int mid = (l + r) >> 1;
    int tqtop = 0;
    for(int i = l; i <= mid; ++i) {
        if(q[i].op == 1) {
            tq[++tqtop] = q[i];
            tq[tqtop].t = i;
        }
    }
    int l1 = tqtop;
    for(int i = mid + 1; i <= r; ++i) {
        if(q[i].op == 2) {
            tq[++tqtop] = q[i];
            tq[tqtop].t = i;
        }
    }
    int l2 = tqtop;

    merge(tq + 1, tq + 1 + l1, tq + 1 + l1, tq + 1 + l2, tq2 + 1);
    for(int j = 1; j <= l2; ++j) {
        tq[j] = tq2[j];
    }
    return l2;
}

inline void after_calc(int l, int r, int tqtop) {
    for(int i = 1; i <= tqtop; ++i) {
        if(tq[i].op == 2)
            q[tq[i].t].ans = tq[i].ans;
    }
    int mid = (l + r) >> 1;
    merge(q + l, q + mid + 1, q + mid + 1, q + r + 1, tq2 + 1);
    for(int j = l; j <= r; ++j) {
        q[j] = tq2[j - l + 1];
    }
}

int calc_upleft(int tqtop) {
    int maxy = -INF;
    for(int i = 1; i <= tqtop; ++i) {
        if(tq[i].op == 2)
            maxy = max(maxy, H - tq[i].y + 1);
    }
    if(maxy != INF) {
        for(int i = 1, nxt; i <= tqtop; i = nxt) {
            for(nxt = i + 1; nxt <= tqtop && tq[i].x == tq[nxt].x; ++nxt);
            for(int j = i; j < nxt; ++j) {
                if(tq[j].op == 1 && H - tq[j].y + 1 <= maxy)
                    update(H - tq[j].y + 1, (-tq[j].x + tq[j].y));
            }
            for(int j = i; j < nxt; ++j) {
                if(tq[j].op == 2)
                    tq[j].ans = min(tq[j].ans, (tq[j].x - tq[j].y) + query(H - tq[j].y + 1));
            }
        }
        for(int i = 1; i <= tqtop; ++i) {
            if(tq[i].op == 1 && H - tq[i].y + 1 <= maxy)
                unupdate(H - tq[i].y + 1);
        }
    }
}

int calc_downleft(int tqtop) {
    int maxy = -INF;
    for(int i = 1; i <= tqtop; ++i) {
        if(tq[i].op == 2)
            maxy = max(maxy, tq[i].y);
    }
    if(maxy != INF) {
        for(int i = 1, nxt; i <= tqtop; i = nxt) {
            for(nxt = i + 1; nxt <= tqtop && tq[i].x == tq[nxt].x; ++nxt);
            for(int j = i; j < nxt; ++j) {
                if(tq[j].op == 1 && tq[j].y <= maxy)
                    update(tq[j].y, (-tq[j].x - tq[j].y));
            }
            for(int j = i; j < nxt; ++j) {
                if(tq[j].op == 2)
                    tq[j].ans = min(tq[j].ans, (tq[j].x + tq[j].y) + query(tq[j].y));
            }
        }
        for(int i = 1; i <= tqtop; ++i) {
            if(tq[i].op == 1 && tq[i].y <= maxy)
                unupdate(tq[i].y);
        }
    }
}

int calc_upright(int tqtop) {
    int maxy = -INF;
    for(int i = 1; i <= tqtop; ++i) {
        if(tq[i].op == 2)
            maxy = max(maxy, H - tq[i].y + 1);
    }
    if(maxy != INF) {
        for(int i = tqtop, nxt; i >= 1; i = nxt) {
            for(nxt = i - 1; nxt >= 1 && tq[i].x == tq[nxt].x; --nxt);
            for(int j = i; j > nxt; --j) {
                if(tq[j].op == 1 && H - tq[j].y + 1 <= maxy)
                    update(H - tq[j].y + 1, (tq[j].x + tq[j].y));
            }
            for(int j = i; j > nxt; --j) {
                if(tq[j].op == 2)
                    tq[j].ans = min(tq[j].ans, (-tq[j].x - tq[j].y) + query(H - tq[j].y + 1));
            }
        }
        for(int i = 1; i <= tqtop; ++i) {
            if(tq[i].op == 1 && H - tq[i].y + 1 <= maxy)
                unupdate(H - tq[i].y + 1);
        }
    }
}

int calc_downright(int tqtop) {
    int maxy = -INF;
    for(int i = 1; i <= tqtop; ++i) {
        if(tq[i].op == 2)
            maxy = max(maxy, tq[i].y);
    }
    if(maxy != INF) {
        for(int i = tqtop, nxt; i >= 1; i = nxt) {
            for(nxt = i - 1; nxt >= 1 && tq[i].x == tq[nxt].x; --nxt);
            for(int j = i; j > nxt; --j) {
                if(tq[j].op == 1 && tq[j].y <= maxy)
                    update(tq[j].y, (tq[j].x - tq[j].y));
            }
            for(int j = i; j > nxt; --j) {
                if(tq[j].op == 2)
                    tq[j].ans = min(tq[j].ans, (-tq[j].x + tq[j].y) + query(tq[j].y));
            }
        }
        for(int i = 1; i <= tqtop; ++i) {
            if(tq[i].op == 1 && tq[i].y <= maxy)
                unupdate(tq[i].y);
        }
    }
}

void solve(int l, int r) {

    if(l == r)
        return;
    int mid = (l + r) >> 1;
    bool allupdate = true;
    for(int i = l; i <= mid; i++) {
        if(q[i].op == 2) {
            allupdate = false;
            break;
        }
    }
    if(!allupdate)
        solve(l, mid);
    else
        sort(q + l, q + 1 + mid);
    bool allupdate2 = true;
    for(int i = mid + 1; i <= r; i++) {
        if(q[i].op == 2) {
            allupdate2 = false;
            break;
        }
    }
    if(!allupdate2)
        solve(mid + 1, r);
    else
        sort(q + mid + 1, q + 1 + r);

    if(allupdate && allupdate2)
        return;
    //这里是没必要每一层都排序的,事实上是每一层直接归并就可以了
    int tqtop = before_calc(l, r);
    calc_upleft(tqtop);
    calc_downleft(tqtop);
    calc_upright(tqtop);
    calc_downright(tqtop);
    after_calc(l, r, tqtop);
    return;
}


int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    int n, m;
    Read(n), Read(m);
    //scanf("%d%d", &n, &m);
    int qtop = 0;
    for(int i = 1; i <= n; ++i) {
        int x, y;
        Read(x), Read(y);
        //scanf("%d%d", &x, &y);
        ++qtop;
        q[qtop].op = 1;
        q[qtop].t = qtop;
        q[qtop].x = x + 1;
        q[qtop].y = y + 1;
        q[qtop].ans = INF;
    }
    for(int i = 1; i <= m; ++i) {
        int op, x, y;
        Read(op), Read(x), Read(y);
        //scanf("%d%d%d", &op, &x, &y);
        ++qtop;
        q[qtop].op = op;
        q[qtop].t = qtop;
        q[qtop].x = x + 1;
        q[qtop].y = y + 1;
        q[qtop].ans = INF;
    }
    build();
    solve(1, qtop);
    sort(q + 1, q + 1 + qtop, cmp);
    for(int i = 1; i <= qtop; ++i) {
        if(q[i].op == 2) {
            Write(q[i].ans);
            //printf("%d\n", q[i].ans);
        }
    }
}

AcWing上面还对空间作出要求,把ans项剥离出去,然后把t和op两个整数合成一个,算一下貌似原来刚好够64MB。原本是:20+20+20+4,改了之后是12+12+12+4+4,是绰绰有余。然后把CDQ剪枝换了一下位置,应该再加上一层常数优化的,就是第一层不检测剪枝直接递归下去,也不进行最后一次归并。然后,还可以统计实际上插入树状数组中的数的个数,当插入的数过多时不进行撤销树状数组,直接整棵重建(毕竟memset比一堆判断、与、加减、赋值的树状数组快得多)

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

namespace FastIO {
#define BUF_SIZE 1000000
    bool IOError = 0;
    inline char NextChar() {
        static char buf[BUF_SIZE], *pl = buf + BUF_SIZE, *pr = buf + BUF_SIZE;
        if(pl == pr) {
            pl = buf, pr = buf + fread(buf, 1, BUF_SIZE, stdin);
            if(pr == pl) {
                IOError = 1;
                return -1;
            }
        }
        return *pl++;
    }
#undef BUF_SIZE

    inline bool Blank(char c) {
        return c == ' ' || c == '\n' || c == '\r' || c == '\t';
    }

    template<class T> inline void Read(T &x) {
        char c;
        while(Blank(c = NextChar()));
        if(!IOError) {
            for(x = 0; '0' <= c && c <= '9'; c = NextChar())
                x = (x << 3) + (x << 1) + c - '0';
        }
    }

    template<class T> inline void Read2(T &x) {
        char c;
        bool f = 0;
        while(Blank(c = NextChar()));
        if(!IOError) {
            if(c == '-') {
                f = 1;
                c = NextChar();
            }
            for(x = 0; '0' <= c && c <= '9'; c = NextChar())
                x = (x << 3) + (x << 1) + c - '0';
        }
        if(f)
            x = -x;
    }

    template<class T> inline void PutChar(T x) {
        if(x > 9)
            PutChar(x / 10);
        putchar(x % 10 + '0');
    }

    template<class T> inline void Write(T &x) {
        PutChar(x);
        putchar('\n');
    }

    template<class T> inline void Write2(T &x) {
        if(x < 0) {
            putchar('-');
            PutChar(-x);
        } else
            PutChar(x);
        putchar('\n');
    }
}

using namespace FastIO;

const int INF = 0x3f3f3f3f;
//将题目的[0,1e6]偏移到[1,1e6+1]
const int H = 1e6 + 1;

int bit[H + 5];

inline void build() {
    memset(bit, INF, sizeof(bit));
}

void update(int x, int v) {
    for(; x <= H; x += x & -x) {
        if(v < bit[x])
            bit[x] = v;
    }
}

void unupdate(int x) {
    for(; x <= H; x += x & -x)
        bit[x] = INF;
}

int query(int x) {
    int res = INF;
    for(; x >= 1; x -= x & -x) {
        if(bit[x] < res)
            res = bit[x];
    }
    return res;
}

const int MAXNM = 1e6;

struct Query {
    int op, x, y;
    bool operator<(const Query &q)const {
        return x < q.x;
    }
} q[MAXNM + 5], tq[MAXNM + 5], tq2[MAXNM + 5];

inline int before_calc(int l, int r) {
    int mid = (l + r) >> 1;
    int tqtop = 0;
    for(int i = l; i <= mid; ++i) {
        if(q[i].op & 1)
            tq[++tqtop] = q[i];
    }
    int l1 = tqtop;
    for(int i = mid + 1; i <= r; ++i) {
        if(!(q[i].op & 1))
            tq[++tqtop] = q[i];
    }
    int l2 = tqtop;

    merge(tq + 1, tq + 1 + l1, tq + 1 + l1, tq + 1 + l2, tq2 + 1);
    for(int j = 1; j <= l2; ++j)
        tq[j] = tq2[j];
    return l2;
}

inline void after_calc(int l, int r, int tqtop) {
    int mid = (l + r) >> 1;
    merge(q + l, q + mid + 1, q + mid + 1, q + r + 1, tq2 + 1);
    for(int j = l; j <= r; ++j)
        q[j] = tq2[j - l + 1];
}

int ans[MAXNM + 5];

//超过该界时不进行取消树状数组,直接重建,因为树状数组有很多加法、判断之类的,而memset快得多
const int BUILDLIMIT = 5e4;

int calc_upleft(int tqtop) {
    int maxy = -INF;
    for(int i = 1; i <= tqtop; ++i) {
        if(!(tq[i].op & 1))
            maxy = max(maxy, H - tq[i].y + 1);
    }
    if(maxy != INF) {
        int cnt = 0;
        for(int i = 1, nxt; i <= tqtop; i = nxt) {
            for(nxt = i + 1; nxt <= tqtop && tq[i].x == tq[nxt].x; ++nxt);
            for(int j = i; j < nxt; ++j) {
                if(tq[j].op & 1 && H - tq[j].y + 1 <= maxy) {
                    update(H - tq[j].y + 1, (-tq[j].x + tq[j].y));
                    ++cnt;
                }
            }
            for(int j = i; j < nxt; ++j) {
                if(!(tq[j].op & 1))
                    ans[tq[j].op >> 1] = min(ans[tq[j].op >> 1], (tq[j].x - tq[j].y) + query(H - tq[j].y + 1));

            }
        }
        if(cnt < BUILDLIMIT) {
            for(int i = 1; i <= tqtop; ++i) {
                if(tq[i].op & 1 && H - tq[i].y + 1 <= maxy)
                    unupdate(H - tq[i].y + 1);
            }
        } else
            build();
    }
}

int calc_downleft(int tqtop) {
    int maxy = -INF;
    for(int i = 1; i <= tqtop; ++i) {
        if(!(tq[i].op & 1))
            maxy = max(maxy, tq[i].y);
    }
    if(maxy != INF) {
        int cnt = 0;
        for(int i = 1, nxt; i <= tqtop; i = nxt) {
            for(nxt = i + 1; nxt <= tqtop && tq[i].x == tq[nxt].x; ++nxt);
            for(int j = i; j < nxt; ++j) {
                if(tq[j].op & 1 && tq[j].y <= maxy) {
                    update(tq[j].y, (-tq[j].x - tq[j].y));
                    ++cnt;
                }
            }
            for(int j = i; j < nxt; ++j) {
                if(!(tq[j].op & 1))
                    ans[tq[j].op >> 1] = min(ans[tq[j].op >> 1], (tq[j].x + tq[j].y) + query(tq[j].y));
            }
        }
        if(cnt < BUILDLIMIT) {
            for(int i = 1; i <= tqtop; ++i) {
                if(tq[i].op & 1 && tq[i].y <= maxy)
                    unupdate(tq[i].y);
            }
        } else
            build();
    }
}

int calc_upright(int tqtop) {
    int maxy = -INF;
    for(int i = 1; i <= tqtop; ++i) {
        if(!(tq[i].op & 1))
            maxy = max(maxy, H - tq[i].y + 1);
    }
    if(maxy != INF) {
        int cnt = 0;
        for(int i = tqtop, nxt; i >= 1; i = nxt) {
            for(nxt = i - 1; nxt >= 1 && tq[i].x == tq[nxt].x; --nxt);
            for(int j = i; j > nxt; --j) {
                if(tq[j].op & 1 && H - tq[j].y + 1 <= maxy) {
                    update(H - tq[j].y + 1, (tq[j].x + tq[j].y));
                    ++cnt;
                }
            }
            for(int j = i; j > nxt; --j) {
                if(!(tq[j].op & 1))
                    ans[tq[j].op >> 1] = min(ans[tq[j].op >> 1], (-tq[j].x - tq[j].y) + query(H - tq[j].y + 1));
            }
        }
        if(cnt < BUILDLIMIT) {
            for(int i = 1; i <= tqtop; ++i) {
                if(tq[i].op & 1 && H - tq[i].y + 1 <= maxy)
                    unupdate(H - tq[i].y + 1);
            }
        } else
            build();
    }
}

int calc_downright(int tqtop) {
    int maxy = -INF;
    for(int i = 1; i <= tqtop; ++i) {
        if(!(tq[i].op & 1))
            maxy = max(maxy, tq[i].y);
    }
    if(maxy != INF) {
        int cnt = 0;
        for(int i = tqtop, nxt; i >= 1; i = nxt) {
            for(nxt = i - 1; nxt >= 1 && tq[i].x == tq[nxt].x; --nxt);
            for(int j = i; j > nxt; --j) {
                if(tq[j].op & 1 && tq[j].y <= maxy) {
                    ++cnt;
                    update(tq[j].y, (tq[j].x - tq[j].y));
                }
            }
            for(int j = i; j > nxt; --j) {
                if(!(tq[j].op & 1))
                    ans[tq[j].op >> 1] = min(ans[tq[j].op >> 1], (-tq[j].x + tq[j].y) + query(tq[j].y));
            }
        }
        if(cnt < BUILDLIMIT) {
            for(int i = 1; i <= tqtop; ++i) {
                if(tq[i].op & 1 && tq[i].y <= maxy)
                    unupdate(tq[i].y);
            }
        } else
            build();
    }
}

void solve(int l, int r, bool firstlayer = false) {
    if(l == r)
        return;
    if(!firstlayer) {
        bool all1 = true;
        for(int i = l; i <= r ; i++) {
            if(!(tq[i].op & 1)) {
                all1 = false;
                break;
            }
        }
        if(all1) {
            sort(q + l, q + r + 1);
            return;
        }
    }
    int mid = (l + r) >> 1;
    solve(l, mid);
    solve(mid + 1, r);
    //这里是没必要每一层都排序的,事实上是每一层直接归并就可以了
    int tqtop = before_calc(l, r);
    calc_upleft(tqtop);
    calc_downleft(tqtop);
    calc_upright(tqtop);
    calc_downright(tqtop);
    if(!firstlayer)
        after_calc(l, r, tqtop);
    return;
}


int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    int n, m;
    Read(n), Read(m);
    //scanf("%d%d", &n, &m);
    int qtop = 0;
    for(int i = 1; i <= n; ++i) {
        int x, y;
        Read(x), Read(y);
        ++qtop;
        q[qtop].op = qtop << 1 | 1;
        q[qtop].x = x + 1;
        q[qtop].y = y + 1;
    }
    for(int i = 1; i <= m; ++i) {
        int op, x, y;
        Read(op), Read(x), Read(y);
        ++qtop;
        q[qtop].op = (qtop << 1) | (op & 1);
        q[qtop].x = x + 1;
        q[qtop].y = y + 1;
    }
    build();
    memset(ans, INF, sizeof(ans[0]) * (n + m + 1));
    solve(1, qtop, true);
    const int MAXANS = 5e6;
    for(int i = 1; i <= qtop; ++i) {
        if(ans[i] <= MAXANS) {
            Write(ans[i]);
        }
    }
}

需要注意的是文件快读和普通快读在O2的时候会导致这题RE,区域赛小心一点。

发现还有一种渐进复杂度更好的办法,或许可以代替掉全修改剪枝,就是把初始点的n个丢进去树状数组把后面m个点中的询问拉出来更新,四个维度各做一次,然后前面n个点就彻底没用了,只对后面的m个点做CDQ分治。

转载于:https://www.cnblogs.com/Inko/p/11469513.html

<think>首先,用户的问题是关于CDQ分治算法的基本原理及其应用场景。我需要根据系统级指令来构建我的回答。 系统级指令关键点: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 - 回答结构清晰,帮助用户逐步解决问题,保证真实可靠。 参考站内引用: - 引用[1]:关于分治思想的描述,包括分块、递归等。 - 引用[2]:关于书籍的引用,不相关。 - 引用[3]:关于下载资源的链接,不相关。 用户真正的需求是理解CDQ分治的概念和原理,所以我需要专注于CDQ分治CDQ分治的基本原理: - CDQ分治是由陈丹琦(Chen Danqi)提出的一种算法,用于处理偏序问题,如三维偏序问题。 - 它是分治思想的一种应用,特别适合解决离线查询问题。 - 核心思想:将问题分成子问题,递归解决,然后合并结果,处理跨越子问题的部分。 应用场景: - 三维偏序问题:例如,给定点集,统计每个点满足某些条件的点数。 - 动态规划优化:如斜率优化等。 - 其他离线查询问题。 我的回答结构: 1. **介绍CDQ分治**:简要说明其背景和目的。 2. **基本原理**:解释算法步骤,使用数学表达式描述。 3. **应用场景**:列出常见应用。 4. **生成相关问题**:在最后添加相关问题。 5. **添加引用**:在引用段落末尾自然添加引用标识。 数学表达式: - 行内数学:如变量、点坐标等,使用$...$。 - 独立公式:如方程,使用$$...$$。 例如: - 行内:点$P_i = (a_i, b_i, c_i)$ - 独立:偏序关系公式。 生成相关问题: - 基于CDQ分治,如"CDQ分治如何优化三维偏序问题?"或"CDQ分治的时间复杂度分析?" 确保回答真实可靠: - 基于算法知识,CDQ分治常用于OI竞赛中处理偏序问题。 引用处理: - 参考引用[1]是关于分治思想的,我可以引用它来支持分治部分。 - 其他引用不相关,但系统说"请尽可能多的参考用户上一次的问题和你的回答",但用户没有上一次问题,所以基于当前引用。 最终回答大纲: - 开头:介绍CDQ分治- 原理部分:分步骤解释,使用LaTeX。 - 应用场景:列举例子。 - 相关问题:在最后添加。 - 结尾:自然结束。</think>### CDQ分治算法的基本原理及其应用场景 CDQ分治(Chen Danqi Divide and Conquer)是由中国计算机科学家陈丹琦提出的一种分治算法变体,主要用于处理离线查询问题,特别是多维偏序问题(如三维偏序)。它在算法竞赛和数据处理中应用广泛,能高效解决一些复杂问题。下面我将逐步解释其基本原理和应用场景,确保内容清晰可靠。 #### 1. CDQ分治的基本原理 CDQ分治基于经典分治思想,但针对偏序问题进行了优化。核心思路是将问题递归分解为子问题,处理子问题内部和子问题之间的影响,最后合并结果。算法主要分为三个步骤:分、治、合。以下是详细解释: - **分(Divide)**:将输入数据(如点集)按某一维度(通常是时间或索引)分成两个大致相等的子集,记为左子集$L$和右子集$R$。例如,给定点集$P = \{P_1, P_2, \dots, P_n\}$,其中每个点$P_i = (a_i, b_i, c_i)$表示三维坐标,我们按第一维$a_i$排序并分割: $$ \text{mid} = \left\lfloor \frac{n}{2} \right\rfloor $$ 这样得到$L = \{P_i \mid i \leq \text{mid}\}$和$R = \{P_i \mid i > \text{mid}\}$。递归地对$L$和$R$应用相同过程。 - **治(Conquer)**:递归处理左子集$L$和右子集$R$,解决子问题内部的偏序关系。例如,在三维偏序问题中,目标是统计每个点$P_i$满足$a_j \leq a_i, b_j \leq b_i, c_j \leq c_i$的点数$j$($j \neq i$)。递归调用确保子集内部的问题被解决。 - **合(Merge)**:合并子问题结果,并处理跨越子集$L$和$R$的影响。这是CDQ分治的关键: - 使用辅助数据结构(如树状数组或线段树)高效处理跨子集贡献。 - 例如,在三维偏序中,合并时需统计$L$中的点对$R$中点的贡献。具体做法: 1. 对$L$和$R$按第二维$b_i$排序。 2. 遍历$R$中的每个点$P_j$,同时维护一个树状数组,用于记录第三维$c_i$的累积值。 3. 对于$P_j$,查询树状数组中满足$c_k \leq c_j$的点数($k$来自$L$),并累加到结果。 这确保了跨子集贡献在$O(n \log n)$时间内完成。 整个算法的时间复杂度为$O(n \log^2 n)$,其中$n$是数据规模。递归公式可表示为: $$ T(n) = 2T\left(\frac{n}{2}\right) + O(n \log n) $$ 其中$O(n \log n)$来自合并步骤的排序和树状数组操作。CDQ分治通过“分而治之”高效处理了偏序关系[^1]。 #### 2. CDQ分治的应用场景 CDQ分治特别适合离线查询问题,其中数据点固定,查询可批量处理。常见应用包括: - **三维偏序问题**:如统计逆序对或点对关系。例如,在竞赛中,给定点集统计每个点“支配”的点数(即所有维度都小于等于该点的点)。CDQ分治能高效解决,时间复杂度优于暴力$O(n^2)$。 - **动态规划优化**:用于优化序列问题,如最长上升子序列(LIS)或斜率优化问题。通过分治处理状态转移,减少计算量。 - **离线查询处理**:在数据库或算法中,处理批量查询,如区间统计或范围查询。CDQ分治将查询排序后递归处理,避免实时开销。 - **其他领域**:在计算几何(如最近点对问题)和机器学习数据预处理中也有应用,但需结合具体算法调整。 CDQ分治的优势在于其通用性和效率,但要求问题具有“可分治性”,即子问题独立且合并步骤可高效实现。实际应用中,常结合树状数组等数据结构提升性能。 #### 相关问题 1. CDQ分治如何处理更高维度的偏序问题(如四维偏序)? 2. CDQ分治普通分治算法的主要区别是什么? 3. CDQ分治在动态规划优化中的具体实现案例? [^1]: 分治思想的核心是递归分解问题,然后合并结果,这在CDQ分治中得到充分体现,如分割点集并递归处理子问题。 [^2]: 算法理论常参考经典书籍,但CDQ分治更常见于竞赛和算法论文。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值