【BZOJ2658】【ZJOI2012】小蓝的好友(mrx)

【题目链接】

【思路要点】

  • 求解不含资源点的矩形个数,用总共的矩形个数减之,得到答案。
  • 考虑扫描线,从上至下枚举矩形的下边界,记每一个横坐标\(x\)的资源点最近一次出现在\((x,Depth_x)\)处。
  • 维护一棵笛卡尔树(Treap),使得父节点的\(Depth\)始终大于子节点的\(Depth\),记每个点的子树大小为\(Size_x\)。
  • 此时,有\(Ans=\sum_{i=1}^{C}(Depth_{father_i}-Depth_i)*\binom{Size_i+1}{2}\),其中\(Ans\)代表以当前纵坐标为下边界的不含资源点的矩形的个数。
  • 现在只存在两种操作,将扫描线下移和将某个点的\(Depth\)修改为扫描线的纵坐标,由于数据随机,树高是\(O(LogC)\)的,暴力旋转即可。显然,在这个过程中,我们也可以顺便维护\(Ans\)。
  • 时间复杂度\(O(NLogC+R+C)\)。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct Treap {
	struct Node {
		int father, child[2];
		int depth, size;
		long long val;
	} a[MAXN];
	int root, cur, n;
	long long sum;
	void update(int root) {
		a[root].size = 1;
		if (a[root].child[0]) a[root].size += a[a[root].child[0]].size;
		if (a[root].child[1]) a[root].size += a[a[root].child[1]].size;
		sum -= a[root].val;
		a[root].val = (a[root].size + 1ll) * a[root].size / 2 * (a[a[root].father].depth - a[root].depth);
		sum += a[root].val;
	}
	void build(int fa, int &root, int l, int r) {
		root = (l + r) / 2;
		a[root].father = fa;
		a[root].size = 1;
		a[root].depth = 0;
		if (root > l) build(root, a[root].child[0], l, root - 1);
		if (root < r) build(root, a[root].child[1], root + 1, r);
		update(root);
	}
	void init(int x) {
		root = sum = 0; n = x;
		build(0, a[root].child[1], 1, n);
	}
	void move() {
		cur++;
		a[root].depth++;
		if (a[root].child[0]) update(a[root].child[0]);
		if (a[root].child[1]) update(a[root].child[1]);
	}
	bool get(int x) {return x == a[a[x].father].child[1]; }
	void reset(int x) {
		static int pos[MAXN];
		int cnt = 0;
		a[x].depth = cur;
		while (a[x].depth > a[a[x].father].depth) {
			int f = a[x].father, g = a[f].father;
			bool tmp = get(x), tnp = get(f);
			a[f].child[tmp] = a[x].child[tmp ^ 1];
			a[a[x].child[tmp ^ 1]].father = f;
			a[x].child[tmp ^ 1] = f;
			a[f].father = x;
			a[x].father = g;
			a[g].child[tnp] = x;
			pos[++cnt] = f;
		}
		pos[++cnt] = x;
		for (int i = 1; i <= cnt; i++) {
			update(pos[i]);
			if (a[pos[i]].child[0]) update(a[pos[i]].child[0]);
			if (a[pos[i]].child[1]) update(a[pos[i]].child[1]);
		}
	}
	long long query() {
		return sum;
	}
} T;
int n, m, k;
vector <int> a[MAXN];
int main() {
	read(n), read(m), read(k);
	for (int i = 1; i <= k; i++) {
		int x, y;
		read(x), read(y);
		a[x].push_back(y);
	}
	T.init(m);
	long long ans = (n + 1ll) * n / 2 * (m + 1ll) * m / 2;
	for (int i = 1; i <= n; i++) {
		T.move();
		for (unsigned j = 0; j < a[i].size(); j++)
			T.reset(a[i][j]);
		ans -= T.query();
	}
	writeln(ans);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值