[BZOJ2658][Zjoi2012]小蓝的好友(mrx)(扫描线+Treap)

Source

https://www.lydsy.com/JudgeOnline/problem.php?id=2658

Solution

First

直觉告诉我们:「至少一个」是不好直接做的。
因此考虑补集转换:用「选出一个子矩形的所有方案数」减去「子矩形内没有资源点的方案数」。
「选出一个子矩形的所有方案数」比较显然:

r × ( r + 1 ) 2 × c × ( c + 1 ) 2

而下面就考虑怎么求「子矩形内没有资源点的方案数」。

Second

考虑使用扫描线来求解。具体的思路就是设一个 p o s ,从第一行移到最后一行,在移动的过程中,维护出:
「所有的子矩形 [ l , p o s ] [ a , b ] (即表示行号从 l p o s ,列号从 a b )中,没有资源点的子矩形个数」。
设这个时候, 定义一个 w [ i ] 1 i c ):
表示第 i 列,行号在 [ 1 , p o s ] 范围内,位置最下(行号最大)的资源点的编号。
这时候,假设固定 a b ,考虑这时候的子矩形 [ l , p o s ] [ a , b ] 产生的贡献。即有多少个合法的 l
可以发现,只要保证 l 大于 w [ a . . . b ] 中的任意一个数即可。
l [ 1 , p o s ] ,因此这时候合法的 l 的个数为:

p o s max [ a , b ]

其中 max [ a , b ] 表示 w 的区间 [ a , b ] 的最小值。
也就是对于这个 p o s ,产生的贡献为:
i = 1 c j = i c { [ p o s max [ i , j ] }

= c × ( c + 1 ) 2 × p o s i = 1 c j = i c max [ i , j ]

于是问题转化为:
给定一个数组 w [ 1... c ] ,初始全部为 0 。然后每次会修改一个 w [ i ] ,或者询问 i = 1 c j = i c max [ i , j ] 的值(也就是 w 的所有子区间的最大值之和)。

Third

可以用一棵二叉树维护这个序列。这棵树具有这个特点:
(1)根节点为这个序列的最大值,设这个最大值为序列的第 k 个元素。
(2)左子树是区间 [ 1 , k 1 ] 构成的二叉树,右子树是区间 [ k + 1 , c ] 构成的二叉树。
简单地说,这棵二叉树的特点:
(1)中序遍历可以得到原序列。
(2)权值满足(大根)堆性质。
(3)以节点 x 对应序列位置为最大值的区间个数为

( s i z e [ l c [ x ] ] + 1 ) × ( s i z e [ r c [ x ] ] + 1 )

s i z e 为子树大小, l c r c 为左右子树。
(4)由(3)得出,如果节点 x 的权值为 q [ x ] ,那么 w 的子区间最大值之和为:
x q [ x ] × ( s i z e [ l c [ x ] ] + 1 ) × ( s i z e [ r c [ x ] ] + 1 )

我们把这棵树叫做 cyf树 cx树 cyx树 pyz树 笛卡尔树。

Fourth

特点(1)(2)让我们想到了什么?
Treap !
考虑用 Treap 维护笛卡尔树。
把权值当做优先级,位置编号当做关键字建立 Treap 。
由于数据随机,因此复杂度可以保证。
在旋转、插入、删除的时候,在 Treap 上计算对序列区间最大值之和的贡献。
复杂度:期望 O ( r + c + n log c )

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
typedef long long ll;
const int N = 4e5 + 5;
int r, c, n, rt, QAQ, lc[N], rc[N], sze[N], pri[N], pyz[N];
ll ans, sum;
struct cyx {int x, y;} a[N];
bool comp(cyx a, cyx b) {return a.x < b.x || (a.x == b.x && a.y < b.y);}
void upt(int x) {
    sze[x] = 1; if (lc[x]) sze[x] += sze[lc[x]]; if (rc[x]) sze[x] += sze[rc[x]];
}
void zig(int &x) {
    sum -= 1ll * (sze[lc[x]] + 1) * (sze[rc[x]] + 1) * pri[x];
    sum -= 1ll * (sze[lc[lc[x]]] + 1) * (sze[rc[lc[x]]] + 1) * pri[lc[x]];
    int y = lc[x]; lc[x] = rc[y]; rc[y] = x; sze[y] = sze[x]; upt(x); x = y;
    sum += 1ll * (sze[lc[x]] + 1) * (sze[rc[x]] + 1) * pri[x];
    sum += 1ll * (sze[lc[rc[x]]] + 1) * (sze[rc[rc[x]]] + 1) * pri[rc[x]];
}
void zag(int &x) {
    sum -= 1ll * (sze[lc[x]] + 1) * (sze[rc[x]] + 1) * pri[x];
    sum -= 1ll * (sze[lc[rc[x]]] + 1) * (sze[rc[rc[x]]] + 1) * pri[rc[x]];
    int y = rc[x]; rc[x] = lc[y]; lc[y] = x; sze[y] = sze[x]; upt(x); x = y;
    sum += 1ll * (sze[lc[x]] + 1) * (sze[rc[x]] + 1) * pri[x];
    sum += 1ll * (sze[lc[lc[x]]] + 1) * (sze[rc[lc[x]]] + 1) * pri[lc[x]];
}
void ins(int &x, int t, int p1, int p2) {
    if (!x) return (void) (sze[x = ++QAQ] = 1, pri[QAQ] = p1,
        pyz[QAQ] = p2, sum += p1); sze[x]++;
    int tmp = sze[lc[x]]; if (t <= tmp) {
        sum += 1ll * pri[x] * (sze[rc[x]] + 1); ins(lc[x], t, p1, p2);
        if (pri[lc[x]] > pri[x] ||
            (pri[lc[x]] == pri[x] && pyz[lc[x]] < pyz[x])) zig(x);
    }
    else {
        sum += 1ll * pri[x] * (sze[lc[x]] + 1);
        ins(rc[x], t - tmp - 1, p1, p2); if (pri[rc[x]] > pri[x] ||
            (pri[rc[x]] == pri[x] && pyz[rc[x]] < pyz[x])) zag(x);
    }
}
int build(int L, int R) {
    int x = ++QAQ, mid = L + R >> 1; if (L == 1 && R == c) rt = x;
    if (L < mid) lc[x] = build(L, mid - 1);
    if (mid < R) rc[x] = build(mid + 1, R);
    pri[x] = 0; pyz[x] = x; upt(x); return x;
}
int findx(int rk) {
    int x = rt; while (x) {
        int tmp = sze[lc[x]]; if (rk == tmp + 1) return x;
        else if (rk <= tmp) x = lc[x]; else x = rc[x], rk -= tmp + 1;
    }
    return -1;
}
void del(int &x, int rk) {
    int tmp = sze[lc[x]]; if (rk == tmp + 1) {
        if (!lc[x] || !rc[x]) sum -= 1ll * pri[x] *
            (sze[lc[x]] + sze[rc[x]] + 1), x = lc[x] + rc[x];
        else {
            (pri[lc[x]] > pri[rc[x]] || (pri[lc[x]] == pri[rc[x]]
                && pyz[lc[x]] < pyz[rc[x]])) ? zig(x) : zag(x); del(x, rk);
        }
        return;
    }
    sze[x]--;
    if (rk <= tmp) sum -= 1ll * pri[x] * (sze[rc[x]] + 1), del(lc[x], rk);
        else sum -= 1ll * pri[x] * (sze[lc[x]] + 1), del(rc[x], rk - tmp - 1);
}
void change(int id, int v) {
    int x = findx(id), _pyz = pyz[x]; del(rt, id); ins(rt, id - 1, v, _pyz);
}
int main() {
    int i; r = read(); c = read(); n = read(); For (i, 1, n)
        a[i].x = read(), a[i].y = read(); sort(a + 1, a + n + 1, comp);
    build(1, c); int lr = 1; For (i, 1, r) {
        while (lr <= n && a[lr].x <= i) change(a[lr].y, i), lr++;
        ans += (1ll * c * (c + 1) >> 1) * i - sum;
    }
    cout << (1ll * r * (r + 1) >> 1) * (1ll * c * (c + 1) >> 1) - ans; return 0;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/80457425