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;
}