#平衡树#洛谷 2611 [ZJOI2012]小蓝的好友


题目

\(R\times C\) 的矩形中,问有多少个子矩形使得存在一个给定点在其中,

保证点随机,\(R,C\leq 4\times 10^4,n\leq 10^5\)


分析

考虑容斥,用总方案减去不含点的子矩形个数,

枚举上边界,那么左右边界可取的下边界取决于点的高度,

可以发现维护点的高度实际上是在维护一棵笛卡尔树,

方案的统计可以在笛卡尔树上进行,由于带修,直接用平衡树即可


代码

#include 
#include 
#include 
using namespace std;
const int N=100011; typedef long long lll;
struct rec{int x,y;}a[N]; lll ans,w[N],c[N];
int ke[N],siz[N],root,las=1,son[N][2],R,C,n,mx;
int iut(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans; 
}
bool cmp(rec x,rec y){return x.xr) return 0; 
	int mid=(l+r)>>1;
	siz[mid]=r-l+1;
	son[mid][0]=build(l,mid-1);
	son[mid][1]=build(mid+1,r);
	return mid;
}
void pup(int now){
	siz[now]=siz[son[now][0]]+siz[son[now][1]]+1;
	w[now]=w[son[now][0]]+w[son[now][1]]+1ll*ke[now]*(siz[son[now][0]]+1)*(siz[son[now][1]]+1);
}
void Split(int now,int kth,int &fi,int &se){
	if (!now) {fi=se=0; return;}
    if (kth>siz[son[now][0]]){
    	fi=now;
    	Split(son[now][1],kth-siz[son[now][0]]-1,son[now][1],se);
	}else{
		se=now;
		Split(son[now][0],kth,fi,son[now][0]);
	}
	pup(now);
}
int Merge(int fi,int se){
	if (!fi||!se) return fi|se;
	if (ke[fi]>ke[se]){
		son[fi][1]=Merge(son[fi][1],se);
		pup(fi);
		return fi;
	}else{
		son[se][0]=Merge(fi,son[se][0]);
		pup(se);
		return se;
	}
}
void update(int x,int z){
	int fi,se,th; Split(root,x-1,fi,se);
	Split(se,1,se,th),w[se]=ke[se]=z;
	root=Merge(Merge(fi,se),th);
}
int main(){
	R=iut(),C=iut(),n=iut(),mx=R>C?R:C;
	for (int i=1;i<=mx;++i) c[i+1]=c[i]+i;
	for (int i=1;i<=n;++i) a[i]=(rec){iut(),iut()};
	sort(a+1,a+1+n,cmp),root=build(1,C);
	for (int l=1,r;l<=n;las=a[l].x,l=r){
		ans+=(c[a[l].x]-c[las])*c[C+1]-w[root]*(a[l].x-las);
		for (r=l;a[r].x==a[l].x;++r) update(a[r].y,a[l].x);
	}
	ans+=(c[R+1]-c[las])*c[C+1]-w[root]*(R+1-las);
	return !printf("%lld",c[R+1]*c[C+1]-ans);
}

相关