Description
终于到达了这次选拔赛的最后一题,想必你已经厌倦了小蓝和小白的故事,为了回馈各位比赛选手,此题的主角是贯穿这次比赛的关键人物——小蓝的好友。
在帮小蓝确定了旅游路线后,小蓝的好友也不会浪费这个难得的暑假。与小蓝不同,小蓝的好友并不想将时间花在旅游上,而是盯上了最近发行的即时战略游戏——
SangoCraft
。但在前往通关之路的道路上,一个小游戏挡住了小蓝的好友的步伐。
“国家的战争其本质是抢夺资源的战争”是整款游戏的核心理念,这个小游戏也不例外。简单来说,用户需要在给定的长方形土地上选出一块子矩形,而系统随机生成了
N
个资源点,位于用户所选的长方形土地上的资源点越多,给予用户的奖励也越多。悲剧的是,小蓝的好友虽然拥有着极其优秀的能力,但同时也有着极差的
RP
,小蓝的好友所选的区域总是没有一个资源点。
终于有一天,小蓝的好友决定投诉这款游戏的制造厂商,为了搜集证据,小蓝的好友想算出至少包含一个资源点的区域的数量。作为小蓝的好友,这自然是你分内之事。
Input
每个输入文件中仅包含一个测试数据。
第一行包含两个由空格隔开的正整数
R,C,N
,表示游戏在一块
[1,R]X[1,C]
的地图上生成了
N
个资源点。
接下来有
N
行,每行包含两个整数
x,y
,表示这个资源点的坐标
(1<=x<=R,1<=Y<=c)
。
Output
输出文件应仅包含一个整数,表示至少包含一个资源点的区域的数量。具体的说,设N个资源点的坐标为(i=1..n),你需要计算有多少个四元组(LB,DB,RB,UB)满足1<=LB<=RB<=R,1<=DB<=UB<=C,且存在一个i使得
LB<=Xi<=RB,DB<=Yi<=UB
均成立
。
Sample Input
5 5 4
1 2
2 3
3 5
4 1
1 2
2 3
3 5
4 1
Sample Output
139
HINT
【数据范围】
对于100%的数据,R,C<=40000,N<=100000,资源点的位置两两不同,且位置为随机生成。
题解:
显然可以补集转化一下,然后考虑枚举上边界。当上边界确定时,可能的区域形成一个直方图。将每一列抽象为点,加入到一颗笛卡尔树中,其中满足堆性质的权值是这一列上矩形的高度,满足二叉搜索树性质的是列的编号。那么以点x为根的子树对答案的贡献ans[x]=ans[lch[x]]+ans[rch[x]]+(dep[fa[x]]-dep[x])*siz[x]*(siz[x]+1)/2。由于数据保证随机,矩形高度这一权值相当于一个随机值,用treap维护即可。
再考虑当扫描线下移的时候,会出现两个事件:一列的高度加一或者清零。我们先给一行打上一个+1标记。对于清零的点,我们将它放到根,再将dep直接置零即可。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=4e5+10;
namespace Treap {
int tot,root;
int lch[N],rch[N],dep[N],tag[N],siz[N];
LL ans[N];
inline LL calc(int fa,int x) {
return 1ll*(dep[x]-dep[fa])*siz[x]*(siz[x]+1)/2;
}
inline void inc(int x,int val) {
tag[x]+=val; dep[x]+=val;
}
inline void Pushdown(int x) {
if (!tag[x]) return;
if (lch[x]) inc(lch[x],tag[x]);
if (rch[x]) inc(rch[x],tag[x]);
tag[x]=0;
}
inline void Pushup(int x) {
siz[x]=1; ans[x]=0;
if (lch[x]) siz[x]+=siz[lch[x]],ans[x]+=ans[lch[x]]+calc(x,lch[x]);
if (rch[x]) siz[x]+=siz[rch[x]],ans[x]+=ans[rch[x]]+calc(x,rch[x]);
}
int Build(int l,int r) {
if (l>r) return 0;
int mid=(l+r)>>1;
lch[mid]=Build(l,mid-1);
rch[mid]=Build(mid+1,r);
Pushup(mid);
return mid;
}
int Merge(int x,int y) {
if (!x||!y) {
Pushup(x+y);
return (x+y);
}
Pushdown(x); Pushdown(y);
if (dep[x]<dep[y]) {
rch[x]=Merge(rch[x],y);
Pushup(x); return x;
}
else {
lch[y]=Merge(x,lch[y]);
Pushup(y); return y;
}
}
void Split(int now,int k,int &x,int &y) {
if (!now) return (void) (x=y=0);
Pushdown(now);
if (siz[lch[now]]+1<=k) {
x=now; Split(rch[now],k-siz[lch[now]]-1,rch[now],y);
Pushup(now);
}
else {
y=now; Split(lch[now],k,x,lch[now]);
Pushup(now);
}
}
LL Query() {
Pushdown(root);
return ans[root]+calc(0,root);
}
}
using namespace Treap;
LL n,m,q,las=1;
LL res=0;
pair<int,int> pnt[N];
void Solve(int row) {
inc(root,1);
while (pnt[las].first==row) {
int x,y,z;
Split(root,pnt[las].second-1,x,y);
Split(y,1,z,y);
dep[z]=0;
root=Merge(Merge(x,z),y);
las++;
}
res-=Query();
}
int main() {
scanf("%lld%lld%lld",&n,&m,&q);
for (int i=1;i<=q;i++) scanf("%d%d",&pnt[i].first,&pnt[i].second);
sort(pnt+1,pnt+q+1);
root=Build(1,m);
res=1ll*n*(n+1)/2*(m*(m+1)/2);
for (int i=1;i<=n;i++) Solve(i);
printf("%lld",res);
return 0;
}