P2611 [ZJOI2012] 小蓝的好友 题解

题意:

具体的说,你需要计算有多少个四元组 \((LB,DB,RB,UB)\) 满足 \(1\le LB\le RB\le R,1\le DB\le UB\le C\) ,且存在一个 \(i\) 使得 \(LB\le xi\le RB,DB\le yi\le UB\) 均成立。

\(1\le R,C\le 4\times 10^4\)\(1\le N\le 10^5\),题目保证资源点的位置两两不同,且位置为随机生成。

分析:

考虑枚举下边界,从上往下枚举。那么对于每个下边界对答案的贡献为:

\[\sum_{L=1}^{m}\sum_{R=L}^{m} (\max_{i=L}^{R} h_{i}) \times \frac{(R-L+1)(R-L+2)}{2} \]

显然可以枚举中间的 \(\max\),然后可以建立笛卡尔树(大根堆),它的 \(pri\)\(h_{i}\)\(val\)\(i\)。这样做后,答案贡献变为

\[\sum_{i=1}^{m} (siz_{ls_{i}}+1) \times (siz_{rs_{i}}+1) \times pri \]

可以发现,这样做能使得一个区间的贡献只被一个节点算到

可以使用 FHQ-Treap,由于点是随机的时间复杂度为 \(O(R \log n)\)

代码:

#include<bits/stdc++.h>
#define int long long
#define N 200005
using namespace std;
int n, m, k, root, ans;
vector<int>p[N];
int siz[N], ls[N], rs[N], val[N], pri[N], sum[N], tot;

int newnode(int Val, int Pri) {
	tot++;
	val[tot] = Val;
	pri[tot] = Pri;
	siz[tot] = 1;
	return tot;
}

void pushup(int u) {
	siz[u] = siz[ls[u]] + siz[rs[u]] + 1;
	sum[u] = sum[ls[u]] + sum[rs[u]] + (siz[ls[u]] + 1) * (siz[rs[u]] + 1) * pri[u];
}

void split(int u, int &x, int &y, int k) {
	if(u == 0) {
		x = y = 0;
		return;
	}
	if(val[u] <= k) {
		x = u;
		split(rs[u], rs[u], y, k);
	}
	else {
		y = u;
		split(ls[u], x, ls[u], k);
	}
	pushup(u);
}

int merge(int x, int y) {
	if(x == 0 || y == 0) return x + y;
	if(pri[x] > pri[y]) {
		rs[x] = merge(rs[x], y);
		pushup(x);
		return x;
	}
	else {
		ls[y] = merge(x, ls[y]);
		pushup(y);
		return y;
	} 
}

void Insert(int Val, int Pri) {
	int x, y;
	split(root, x, y, Val);
	root = merge(merge(x, newnode(Val, Pri)), y);
}

void Erase(int Val) {
	int x, y, z;
	split(root, x, y, Val - 1);
	split(y, y, z, Val);
	root = merge(x, z);
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	srand(time(0));
	cin >> n >> m >> k;
	
	for(int i = 1, x, y; i <= k; i++) {
		cin >> x >> y;
		p[x].push_back(y);
	}
	
	for(int i = 1; i <= m; i++) Insert(i, rand());

	
	for(int i = 1; i <= m; i++) pri[i] = 0, sum[i] = 0;
	for(int i = 1; i <= n; i++) {
		for(auto y : p[i]) {
			Erase(y);
			Insert(y, i);
		}
		ans += sum[root];
	}
	
	cout << ans;
	return 0;
}
posted @ 2024-04-12 10:54  小超手123  阅读(10)  评论(0编辑  收藏  举报