bzoj 2658: [Zjoi2012]小蓝的好友(mrx) Treap

7 篇文章 0 订阅

       Treap神题。。。首先将问题转化为补集即求不含一个坏点的矩形个数。

       考虑这道题目的暴力;显然我们枚举矩形的下边界,得到每一列可以向上拓展多少(视为高度),用单调队列可以求出对于列i向左向右拓展到多少然后就能求出该下边界的答案了。

       注意到如果把向左能拓展到的看成是在dfs一颗树时入栈的序号,向右能拓展到的看成是在dfs时出栈的序号,那么显然这可以看成一棵树的dfs序;由于特殊性质可以证明这是一颗二叉树,而且父亲节点的高度总小于儿子的高度。

       如果把高度看成键值,这就是一颗Treap的结构!接下来从Treap的角度来看,我们一行一行操作:

       首先向下移一行时,相当于直接给所有点高度+1,打标记即可;然后对于这一行的每一个坏点,相当于把它的高度变成0,然后向上旋转(如果改行没有其他0显然是转到根)。

       由于数据随机化所以是单次O(logN)的。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define N 40005
#define calc(x) ((ll)(x)*((x)+1)>>1)
using namespace std;

struct node{ int x,y; }a[100005];
int n,m,cnt,rt,sz[N],ht[N],c[N][2],icr[N]; ll sum[N];
bool cmp(const node &u,const node &v){
	return u.x<v.x || u.x==v.x && u.y<v.y;
}
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
void maintain(int x){
	sz[x]=1; sum[x]=0; int l=c[x][0],r=c[x][1];
	if (l){
		sz[x]+=sz[l]; sum[x]+=sum[l]+calc(sz[l])*(ht[l]-ht[x]);
	}
	if (r){
		sz[x]+=sz[r]; sum[x]+=sum[r]+calc(sz[r])*(ht[r]-ht[x]);
	}
}
void ins(int x,int v){
	ht[x]+=v; icr[x]+=v;
}
void pushdown(int x){
	if (icr[x]){
		ins(c[x][0],icr[x]); ins(c[x][1],icr[x]); icr[x]=0;
	}
}
void rotate(int &k,int l){
	int t=c[k][l];
	c[k][l]=c[t][l^1]; c[t][l^1]=k;
	maintain(k); maintain(t); k=t;
}
void mdy(int &k,int x){
	pushdown(k); int tmp=sz[c[k][0]]+1;
	if (x==tmp){
		ht[k]=0; maintain(k); return;
	}
	if (x<tmp){
		mdy(c[k][0],x);
		if (ht[c[k][0]]<ht[k]) rotate(k,0); else maintain(k);
	} else{
		mdy(c[k][1],x-tmp);
		if (ht[c[k][1]]<ht[k]) rotate(k,1); else maintain(k);
	}
}
void build(int &k,int l,int r){
	if (l>r){ k=0; return; }
	k=(l+r)>>1;
	build(c[k][0],l,k-1); build(c[k][1],k+1,r);
	sz[k]=sz[c[k][0]]+sz[c[k][1]]+1;
}
int main(){
	m=read(); n=read(); cnt=read(); int i,j;
	for (i=1; i<=cnt; i++){
		a[i].x=read(); a[i].y=read();
	}
	sort(a+1,a+cnt+1,cmp);
	ll ans=calc(m)*calc(n);
	build(rt,1,n);
	for (i=j=1; i<=m; i++){
		ins(rt,1);
		for (; j<=cnt && a[j].x==i; j++) mdy(rt,a[j].y);
		ans-=sum[rt]+calc(sz[rt])*ht[rt];
	}
	printf("%lld\n",ans);
	return 0;
}


by lych

2016.5.30

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值