Source
https://www.lydsy.com/JudgeOnline/problem.php?id=2658
Solution
First
直觉告诉我们:「至少一个」是不好直接做的。
因此考虑补集转换:用「选出一个子矩形的所有方案数」减去「子矩形内没有资源点的方案数」。
「选出一个子矩形的所有方案数」比较显然:
而下面就考虑怎么求「子矩形内没有资源点的方案数」。
Second
考虑使用扫描线来求解。具体的思路就是设一个
,从第一行移到最后一行,在移动的过程中,维护出:
「所有的子矩形
(即表示行号从
到
,列号从
到
)中,没有资源点的子矩形个数」。
设这个时候, 定义一个
(
):
表示第
列,行号在
范围内,位置最下(行号最大)的资源点的编号。
这时候,假设固定
和
,考虑这时候的子矩形
产生的贡献。即有多少个合法的
。
可以发现,只要保证
大于
中的任意一个数即可。
而
,因此这时候合法的
的个数为:
其中 表示 的区间 的最小值。
也就是对于这个 ,产生的贡献为:
于是问题转化为:
给定一个数组 ,初始全部为 。然后每次会修改一个 ,或者询问 的值(也就是 的所有子区间的最大值之和)。
Third
可以用一棵二叉树维护这个序列。这棵树具有这个特点:
(1)根节点为这个序列的最大值,设这个最大值为序列的第
个元素。
(2)左子树是区间
构成的二叉树,右子树是区间
构成的二叉树。
简单地说,这棵二叉树的特点:
(1)中序遍历可以得到原序列。
(2)权值满足(大根)堆性质。
(3)以节点
对应序列位置为最大值的区间个数为
为子树大小, 和 为左右子树。
(4)由(3)得出,如果节点 的权值为 ,那么 的子区间最大值之和为:
我们把这棵树叫做
Fourth
特点(1)(2)让我们想到了什么?
Treap !
考虑用 Treap 维护笛卡尔树。
把权值当做优先级,位置编号当做关键字建立 Treap 。
由于数据随机,因此复杂度可以保证。
在旋转、插入、删除的时候,在 Treap 上计算对序列区间最大值之和的贡献。
复杂度:期望
。
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;
}