模板 - CDQ分治

https://nanti.jisuanke.com/t/41356

#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 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');
    }

}

using namespace FastIO;

const int MAXN = 2e5;

//因为题目的数据有0,所以全部+1偏移了一位
const int H = 2e5 + 1;
int bit[H + 5];

inline void clear() {
    memset(bit, 0, sizeof(bit));
}

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

inline void set0(int x) {
    for(; x <= H; x += x & -x) {
        bit[x] = 0;
    }
}

inline int sum(int x) {
    int res = 0;
    for(; x ; x -= x & -x) {
        res += bit[x];
    }
    return res;
}

struct Query {
    int top, x, d1;
    bool operator<(const Query &q)const {
        return x < q.x;
    }
} q[5 * MAXN + 5], tq[5 * MAXN + 5];

int ans[MAXN + 5];
bool vis[MAXN + 5];

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

void solve(const int &l, const int &r, bool fl = false) {
    if(l == r)
        return;
    if(!fl) {
        //only update,这个剪枝应该功能强大
        bool ou = true;
        for(int i = l; i <= r ; i++) {
            //发现询问操作,break
            if((q[i].top & 3) >= 2) {
                ou = false;
                break;
            }
        }
        if(ou) {
            sort(q + l, q + r + 1);
            return;
        }
    }
    int mid = (l + r) >> 1;
    solve(l, mid);
    solve(mid + 1, r);

    int i = l, j = mid + 1, top = 0;
    while(i <= mid || j <= r) {
        if(i > mid || (j <= r && q[j].x < q[i].x)) {
            //把右边的询问加进来
            if((q[j].top & 3) >= 2)
                tq[++top] = q[j];
            ++j;
        } else {
            //把左边的修改加进来
            if((q[i].top & 3) <= 1)
                tq[++top] = q[i];
            ++i;
        }
    }

    //真正进行的修改操作的次数,用来辅助判断直接重建,可能是负优化
    //int cnt=0;
    for(int i = 1, nxt; i <= top; i = nxt) {
        for(nxt = i + 1; nxt <= top && tq[i].x == tq[nxt].x; ++nxt);
        for(int j = i; j < nxt; ++j) {
            //把修改操作执行
            if((tq[j].top & 3) == 1) {
                add(tq[j].d1, 1);
                //++cnt;
            } else if((tq[j].top & 3) == 0) {
                add(tq[j].d1, -1);
                //++cnt;
            }
        }
        for(int j = i; j < nxt; ++j) {
            //把询问操作执行
            if((tq[j].top & 3) == 2) {
                //前面的转换已经把l可能产生的贡献已经清除了
                ans[tq[j].top >> 2] -= sum(tq[j].d1);
            } else if((tq[j].top & 3) == 3) {
                ans[tq[j].top >> 2] += sum(tq[j].d1);
                //op2和op3都是成对出现的,只对其中一个赋值就可以了
                vis[tq[j].top >> 2] = true;
            }
        }
    }

    if(!fl) {
        if(cnt < BUILDLIMIT) {
            for(int j = 1; j <= top; ++j) {
                //对应撤销修改操作
                if((tq[j].top & 3) == 1)
                    add(tq[j].d1, -1);
                else if((tq[j].top & 3) == 0)
                    add(tq[j].d1, +1);
            }
        } else {
            //直接重建
            clear();
        }
        merge(q + l, q + mid + 1, q + mid + 1, q + r + 1, tq + 1);
        for(int j = l; j <= r; ++j)
            q[j] = tq[j - l + 1];
    }
    return;
}

int a[MAXN + 5], b[MAXN + 5];

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku

    int n, m;
    Read(n), Read(m);
    int qtop = 0;

    for(int i = 1; i <= n; ++i) {
        Read(a[i]);
        b[i] = (a[i] == a[i - 1]) ? 0 : a[i];

        q[++qtop].top = 1;
        q[qtop].x = i;
        q[qtop].d1 = b[i] + 1;
    }

    for(int i = 1, op; i <= m; ++i) {
        Read(op);
        if(op == 1) {
            int x, y;
            Read(x), Read(y);
            q[++qtop].top = i << 2;
            q[qtop].x = x;
            q[qtop].d1 = b[x] + 1;

            q[++qtop].top = i << 2;
            q[qtop].x = x + 1;
            q[qtop].d1 = b[x + 1] + 1;

            a[x] = y;
            b[x] = (a[x] == a[x - 1]) ? 0 : a[x];
            b[x + 1] = (a[x + 1] == a[x]) ? 0 : a[x + 1];

            q[++qtop].top = i << 2 | 1;
            q[qtop].x = x;
            q[qtop].d1 = b[x] + 1;

            q[++qtop].top = i << 2 | 1;
            q[qtop].x = x + 1;
            q[qtop].d1 = b[x + 1] + 1;
        } else {
            int l, r, x, y;
            Read(l), Read(r), Read(x), Read(y);
            //op2是要被减去的,op3是要被加上的
            q[++qtop].top = i << 2 | 2;
            //这里询问的就是要l而不是l-1,为了把l可能产生的贡献去掉然后加回来
            q[qtop].x = l;
            q[qtop].d1 = y + 1;

            q[++qtop].top = i << 2 | 3;
            //这里询问的就是要l而不是l-1,为了把l可能产生的贡献去掉然后加回来
            q[qtop].x = l;
            q[qtop].d1 = x - 1 + 1;

            q[++qtop].top = i << 2 | 3;
            q[qtop].x = r;
            q[qtop].d1 = y + 1;

            q[++qtop].top = i << 2 | 2;
            q[qtop].x = r;
            q[qtop].d1 = x - 1 + 1;

            //把l的贡献加回来
            if(x <= a[l] && a[l] <= y)
                ++ans[i];
        }
    }

    solve(1, qtop, true);
    for(int i = 1; i <= m; ++i) {
        if(vis[i])
            Write(ans[i]);
    }
}

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值