[BZOJ1176][[Balkan2007]Mokia][CDQ分治]

50 篇文章 0 订阅
2 篇文章 0 订阅

[BZOJ1176][[Balkan2007]Mokia][CDQ分治]

题目大意:

维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000.

思路:

一道双倍经验题,另一题提交地址:http://www.lydsy.com/JudgeOnline/problem.php?id=2683

这应该是一道CDQ分治入门题了吧,CDQ介绍地址:http://blog.csdn.net/g1n0st/article/details/56839817

和BZOJ2716类似,为了将修改维护成询问的前缀,这道题也需要引入分治的思想。如下图所示,一个坐标为 (x1,y1,x2,y2) 内的矩形,可以拆分为 s1(0,0,x2,y1) s2(0,0,x1,y2) s3(0,0,x1,y1) s4(0,0,x2,y2) 四个子矩形。利用容斥原理,求原矩形内的点集,就相当于求 s3+s4s1s2

考虑到四个子矩形的左下角坐标都是 (0,0) ,那么就是把一个询问的矩形拆成四个询问点,询问第一象限内每个询问点左下角的点集,然后容斥一下就好了。

代码:

完。

#include <bits/stdc++.h>
#define lowbit(x) ((x) & (-x))
using namespace std;
const int Maxn = 2000010, Maxm = 200010;
inline char get(void) {
    static char buf[1000000], *p1 = buf, *p2 = buf;
    if (p1 == p2) {
        p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin);
        if (p1 == p2) return EOF;
    }
    return *p1++;
}
inline void read(int &x) {
    x = 0; static char c; bool minus = false;
    for (; !(c >= '0' && c <= '9'); c = get()) if (c == '-') minus = true;
    for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get()); if (minus) x = -x;
}
int t[Maxn], n, ans[Maxn];
inline void insert(int x, int v) {
    for (; x <= n; x += lowbit(x)) t[x] += v;
}
inline int query(int x) {
    static int sum; sum = 0;
    for (; x; x -= lowbit(x)) sum += t[x];
    return sum;
}
struct Abcd {
    int x, y, op, id, bl, v;
    Abcd(void) {}
    Abcd(int x, int y, int v, int op, int id, int bl) :
        x(x), y(y), v(v), op(op), id(id), bl(bl) {}
    friend bool operator < (const Abcd &a, const Abcd &b) {
        if (a.x == b.x && a.y == b.y) return a.id < b.id;
        if (a.x == b.x) return a.y < b.y;
        return a.x < b.x;
    }
} oo[Maxm << 2], tmp[Maxm << 2];
int tot, bl;
inline void solve(int l, int r) {
    if (l == r) return ;
    int mid = (l + r) >> 1;
    for (int i = l; i <= r; i++) {
        if (oo[i].op == 1 && oo[i].id <= mid) insert(oo[i].y, oo[i].v);
        if (oo[i].op == 2 && oo[i].id > mid) {
            if (oo[i].v) ans[oo[i].bl] += query(oo[i].y);
            else ans[oo[i].bl] -= query(oo[i].y);
        }
    }
    for (int i = l; i <= r; i++) {
        if (oo[i].op == 1 && oo[i].id <= mid) insert(oo[i].y, -oo[i].v);
    }
    int l1 = l, l2 = mid + 1;
    for (int i = l; i <= r; i++) {
        if (oo[i].id <= mid) tmp[l1++] = oo[i];
        else tmp[l2++] = oo[i];
    }
    for (int i = l; i <= r; i++) {
        oo[i] = tmp[i];
    }
    solve(l, mid), solve(mid + 1, r);
}
int main(void) {
    //freopen("in.txt", "r", stdin);
    read(n); read(n); int op, x, y, v, x1, y1;
    while (1) {
        read(op);
        if (op == 3) break;
        if (op == 1) {
            read(x), read(y), read(v);
            oo[++tot] = Abcd(x, y, v, 1, tot, 0);
        } else {
            read(x), read(y), read(x1), read(y1);
            oo[++tot] = Abcd(x1, y1, 1, 2, tot, ++bl);
            oo[++tot] = Abcd(x - 1, y - 1, 1, 2, tot, bl);
            oo[++tot] = Abcd(x - 1, y1, 0, 2, tot, bl);
            oo[++tot] = Abcd(x1, y - 1, 0, 2, tot, bl);
        }
    }
    sort(oo + 1, oo + 1 + tot);
    solve(1, tot);
    for (int i = 1; i <= bl; i++) {
        printf("%d\n", ans[i]);
    }
    return 0;
}

By g1n0st

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值