题目大意
求一个n*m网格里有多少矩阵包含至少一个黑格子。
数据保证随机
Treap
可以想到求有多少矩阵不包含任何黑格子。
直接跑单调栈会TLE。
我们可以针对某一行,每个格子求出其最长上升的距离,得到一个数组up。
然后,对up数组进行笛卡尔树建立。
那么,我们知道对于笛卡尔树中任意一个结点x,其对答案贡献为
(h[x]−h[fa[x]])∗size[x]∗(size[x]+1)/2
我们记sum[x]表示以x为根的子树中所有结点对答案的贡献(实现中由于不保存父亲指针,所以不包含x对答案的贡献)
这样一行的答案可以算出了。
从上往下做,每次相当于全部格子的值+1,同时有一些格子值变为0。
treap就是一种笛卡尔树,用treap维护即可。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=40000+10,maxq=100000+10;
int i,j,k,l,t,n,m,p,top,tot,root;
int h[maxn],go[maxq],next[maxq];
int key[maxn],tree[maxn][2],size[maxn],ad[maxn];
ll ans,now,sum[maxn];
int count(int x){
return x*(x+1)/2;
}
void add(int x,int y){
go[++tot]=y;
next[tot]=h[x];
h[x]=tot;
}
void mark(int x,int y){
key[x]+=y;
ad[x]+=y;
}
void down(int x){
if (ad[x]){
if (tree[x][0]) mark(tree[x][0],ad[x]);
if (tree[x][1]) mark(tree[x][1],ad[x]);
ad[x]=0;
}
}
void update(int x){
size[x]=1;sum[x]=0;
if (tree[x][0]){
size[x]+=size[tree[x][0]];
sum[x]+=sum[tree[x][0]]+(ll)count(size[tree[x][0]])*(key[tree[x][0]]-key[x]);
}
if (tree[x][1]){
size[x]+=size[tree[x][1]];
sum[x]+=sum[tree[x][1]]+(ll)count(size[tree[x][1]])*(key[tree[x][1]]-key[x]);
}
}
void rotate(int &x,int z){
int y=tree[x][z];
tree[x][z]=tree[y][1-z];
tree[y][1-z]=x;
update(x);
update(y);
x=y;
}
void change(int &x,int y){
down(x);
if (y==size[tree[x][0]]+1){
key[x]=0;
update(x);
return;
}
else if (y<=size[tree[x][0]]){
change(tree[x][0],y);
rotate(x,0);
}
else{
change(tree[x][1],y-size[tree[x][0]]-1);
rotate(x,1);
}
}
void build(int &x,int l,int r){
if (l>r){
x=0;
return;
}
x=(l+r)/2;
build(tree[x][0],l,x-1);
build(tree[x][1],x+1,r);
update(x);
}
int main(){
scanf("%d%d%d",&n,&m,&p);
fo(i,1,p){
scanf("%d%d",&j,&k);
add(j,k);
}
ans=(ll)count(n)*count(m);
build(root,1,m);
fo(i,1,n){
mark(root,1);
t=h[i];
while (t){
change(root,go[t]);
t=next[t];
}
ans-=sum[root]+(ll)count(size[root])*key[root];
}
printf("%lld\n",ans);
}