第一考虑容斥原理。
然后扫描线扫过去,用一棵可持久化平衡树treap维护搞。
http://wjmzbmr.com/archives/zjoi_2012_round_1_mrx_detailed_problem_solutions/
陈老师的说的很不错。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
inline void read(long long &x){
x=0;
long long f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
x*=f;
}
const long long N=100000+1000;
struct treap{
long long son[N][2];
long long size[N];
long long ans[N];
long long delta[N];
long long root;
long long h[N];
long long v[N];
void add(long long p,long long add){
h[p]+=add;
delta[p]+=add;
}
void pushdown(long long p){
if(delta[p]){
if(son[p][0]){
add(son[p][0],delta[p]);
}
if(son[p][1]){
add(son[p][1],delta[p]);
}
delta[p]=0;
}
}
void update(long long p){
size[p]=1;
ans[p]=0;
pushdown(p);
long long x=son[p][0];
long long y=son[p][1];
if(x){
size[p]+=size[x];
ans[p]=ans[p]+ans[x]+size[x]*(size[x]+1)/2*(h[x]-h[p]);
}
if(y){
size[p]+=size[y];
ans[p]=ans[p]+ans[y]+size[y]*(size[y]+1)/2*(h[y]-h[p]);
}
}
pair<long long,long long> split(long long p,long long k){
if(p==0){
return make_pair(0,0);
}
pushdown(p);
pair<long long,long long> temp;
if(size[son[p][0]]+1<=k){
temp=split(son[p][1],k-size[son[p][0]]-1);
son[p][1]=temp.first;
update(p);
return make_pair(p,temp.second);
}
else{
temp=split(son[p][0],k);
son[p][0]=temp.second;
update(p);
return make_pair(temp.first,p);
}
}
long long merge(long long a,long long b){
if(a==0){
update(b);
return b;
}
if(b==0){
update(a);
return a;
}
pushdown(a);
pushdown(b);
if(h[a]<h[b]){
son[a][1]=merge(son[a][1],b);
update(a);
return a;
}
else{
son[b][0]=merge(a,son[b][0]);
update(b);
return b;
}
}
long long getans(){
pushdown(root);
return ans[root]+h[root]*size[root]*(size[root]+1)/2;
}
}A;
long long m,n;
long long ans;
pair<long long,long long> a[N];
long long q;
/*
引理:容斥原理
正难则反。
求出所有的矩阵数并且减去含0的矩阵数。
所有矩阵数求法:
对于n选取一段:有(n+1)*n/2种选法。
*/
int main(){
read(n);
read(m);
read(q);
for(long long i=1;i<=q;i++){
read(a[i].first);
read(a[i].second);
}
sort(a+1,a+1+q);
ans=(n+1)*n/2*(m+1)*m/2;
/*
利用扫描线求出当前的这个点矩阵可延伸高度。
*/
/*
实际是维护了一棵笛卡尔树。
对于100%数据随机。
那么显然可以利用坐标当做fix来维护treap的时间复杂度。
*/
for(long long i=1;i<=m;i++){
A.root=A.merge(A.root,i);
A.update(A.root);
}
/*
我选用了第一维作为扫描的目标.
*/
for(long long i=1,j=1;i<=n;i++){
A.add(A.root,1);
for(;a[j].first==i;j++){
pair<long long,long long> temp1=A.split(A.root,a[j].second-1);
pair<long long,long long> temp2=A.split(temp1.second,1);
A.h[temp2.first]=0;
A.root=A.merge(temp1.first,temp2.first);
A.root=A.merge(A.root,temp2.second);
}
ans-=A.getans();
}
printf("%lld",ans);
}