省选专练ZJOI2012小蓝的好友

本文介绍了一个利用容斥原理结合Treap数据结构解决的矩阵问题。通过扫描线算法,在每个节点维护一棵可持久化Treap,解决了含0矩阵计数问题。文章详细展示了如何使用Treap进行区间修改和查询,最终得出不含0的矩阵数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一考虑容斥原理。

然后扫描线扫过去,用一棵可持久化平衡树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);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值