P2611-[ZJOI2012]小蓝的好友【Treap,扫描线】

正题

题目链接:https://www.luogu.com.cn/problem/P2611


题目大意

\(r*c\)的网格上有\(n\)个标记点,然后求有多少个矩形包含至少一个标记点。

\(1\leq r,c\leq 4\times 10^4,1\leq n\leq 10^5\)

保证数据随机


解题思路

枚举下边界,考虑上面可行的范围,发现对于左右边界\([l,r]\),可行的上边界是\(\leq max\{a_i\}(i\in[l,r])\)

也就是一个\(a_i\)可以支配的范围是直到它左右边第一个比它大的区域,如果弄出\(a_i\)的笛卡尔树来可以很快实现。

要支持动态插入的笛卡尔树(这个东西显然不存在),转念一想,好像\(Treap\)就是一个支持插入的笛卡尔树?

但是时间复杂度无法保证...其实可以,因为\(Treap\)本来就是随机\(dat\)值来做笛卡尔树的,这里保证了数据随机,所以时间复杂度是\(O(n\log c)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1e5+10;
struct node{
	ll x,y;
}a[N];
ll r,c,n,t[N][2],siz[N],w[N],dat[N],v[N],ans;
void PushUp(ll x){
	siz[x]=siz[t[x][0]]+siz[t[x][1]]+1;
	w[x]=dat[x]*(siz[t[x][0]]+1)*(siz[t[x][1]]+1);
	v[x]=w[x]+v[t[x][0]]+v[t[x][1]];
	return;
}
void zig(ll &x){
	ll y=t[x][0];
	t[x][0]=t[y][1];t[y][1]=x;x=y;
	PushUp(t[x][1]);PushUp(x);return;
}
void zag(ll &x){
	ll y=t[x][1];
	t[x][1]=t[y][0];t[y][0]=x;x=y;
	PushUp(t[x][0]);PushUp(x);return;
}
void Change(ll &x,ll pos,ll val){
	if(x==pos){
		dat[x]=val;
		PushUp(x);return;
	}
	if(pos<x){
		Change(t[x][0],pos,val);
		if(dat[t[x][0]]>dat[x])zig(x);
	}
	else{
		Change(t[x][1],pos,val);
		if(dat[t[x][1]]>dat[x])zag(x);
	}
	PushUp(x);return;
}
bool cmp(node x,node y)
{return x.x<y.x;}
signed main()
{
	scanf("%lld%lld%lld",&r,&c,&n);
	for(ll i=1;i<=n;i++)
		scanf("%lld%lld",&a[i].x,&a[i].y);
	for(ll i=1;i<c;i++)t[i][1]=i+1,siz[i]=c-i+1;
	sort(a+1,a+1+n,cmp);ll p=1,rt=1;siz[c]=1;
	for(ll i=1;i<=r;i++){
		while(p<=n&&a[p].x<=i){
			Change(rt,a[p].y,a[p].x);
			p++;
		}
		ans+=v[rt];
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2021-02-18 21:00  QuantAsk  阅读(42)  评论(0编辑  收藏  举报