CF527C Glass Carving(线段树or模拟+大根堆)

简化题目

有一块w*h的玻璃,每次横着切一刀(H)或者竖着切一刀(V),没有两次相同的切割,求最大的矩形碎片面积。 样例中第一行是w,h(玻璃大小)和n(切割次数),字母后的数字表示距下边缘(H)/左边缘(V)的距离。
输入输出样例
输入样例 #1
4 3 4
H 2
V 2
V 3
V 1
输出样例 #1
8
4
4
2
输入样例 #2
7 6 5
H 4
V 3
V 5
H 2
V 1
输出样例 #2
28
16
12
6
4
说明
Picture for the first sample test:
Picture for the second sample test:
博主小注:这里的矩阵碎片指的是在切完一刀之后剩下来的若干矩阵。
题目传送门

思路

一开始的时候看到这道题目,我觉得应该就是一个模拟的题目,我并没有想到其他的比较神奇的思路……

模拟的思路
由于宽和高是相互之间没有冲突的,所以,我们每次找最大的矩阵的面积就是找到此时存在的最大的宽和高,二者相乘就是我们的答案。

所以,我们可以开两个大根堆分别记录下宽的最大值以及长的最大值,每次询问的时候弹出堆顶即可,弹出之后我们再把他们压回去。

那么,此时问题来了,我们要如何修改呢??
所以,我就想到了可以用两个vector数组模拟我们切的过程,每次找到要切的那个长度,我们把它弹出之后,在把新的两个长度压到我们的vector中,这样就实现了修改的操作。

这个方法似乎又暴露出了新的问题:对于堆中的元素,我们要怎么修改?我们要如何准确的找出我们要删除的元素??

  1. 对于堆中的元素,有一位神仙告诉我说:我们可以建一个包含删除的元素的堆2,(把之前的堆叫做是堆1)当堆1和堆2中的堆顶元素相同时,我们就直接把这个元素同时弹出,不再使用这个元素;对于添加操作的话就直接压到堆中就好了。
  2. 对于找我们所要删除的元素的话,我们可以二分查找。内个神仙告诉我说:你可以使用set……但是,我不会……所以,我的思路就是我们可以对每一个矩阵碎片的高和宽进行倒序前缀和维护,之后,二分查找。

但是,这道题的模拟的思路,我仅仅只是在思想上A掉了它,不知道有没有某位大神可以实际操作一下,我感觉有点麻烦……

线段树的思路
我们可以把这题目转换一下,转换到区间上的问题。

我们可以把高和宽抽象成是一个区间,大小为[1, n-1].(可以思考一下为什么?)
我们把矩阵内部的点看成是0, 我们切的位置看成是1,那么问题此时就变成求一段区间内的0最大连续长度+1。

之后,我们对高和宽分别维护一个连续的0的个数的最大值, a n s = ( l e n h + 1 ) ∗ ( l e n w + 1 ) ans=(lenh+1)*(lenw+1) ans=(lenh+1)(lenw+1)

code

#include<bits/stdc++.h>
using namespace std;

const int nn=200003;
typedef long long ll;

int w, h, Q;

inline int read()
{
	int x=0,f=1; char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
	return x*f;
}

struct tree
{
	bool flag;
	int l, r;
	ll lm, rm, maxx;
}th[nn*4], tw[nn*4];

inline void update1(int p)
{
	th[p].flag=th[p<<1].flag&th[p<<1|1].flag;
	th[p].lm=th[p<<1].flag?th[p<<1].maxx+th[p<<1|1].lm:th[p<<1].lm;
	th[p].rm=th[p<<1|1].flag?th[p<<1|1].maxx+th[p<<1].rm:th[p<<1|1].rm;
	th[p].maxx=max(max(th[p<<1].maxx, th[p<<1|1].maxx), th[p<<1].rm+th[p<<1|1].lm);/**/
}

inline void build1(int p, int l, int r)
{
	th[p].l=l, th[p].r=r;
	if(l==r)
	{
		th[p].flag=true;
		th[p].lm=th[p].rm=th[p].maxx=1;
		return ;
	}
	int mid=(l+r)>>1;
	build1(p<<1, l, mid);
	build1(p<<1|1, mid+1, r);
	update1(p);
}

inline void change1(int p, int x)
{
	if(th[p].l==th[p].r)
	{
		th[p].flag=false;
		th[p].lm=th[p].rm=th[p].maxx=0;
		return ;
	}
	int mid=(th[p].l+th[p].r)>>1;
	if(x<=mid)	change1(p<<1, x);
	else change1(p<<1|1, x);
	update1(p);
}

//

inline void update2(int p)
{
	tw[p].flag=tw[p<<1].flag&tw[p<<1|1].flag;
	tw[p].lm=tw[p<<1].flag?tw[p<<1].maxx+tw[p<<1|1].lm:tw[p<<1].lm;
	tw[p].rm=tw[p<<1|1].flag?tw[p<<1|1].maxx+tw[p<<1].rm:tw[p<<1|1].rm;
	tw[p].maxx=max(max(tw[p<<1].maxx, tw[p<<1|1].maxx), tw[p<<1].rm+tw[p<<1|1].lm);/**/
}

inline void build2(int p, int l, int r)
{
	tw[p].l=l, tw[p].r=r;
	if(l==r)
	{
		tw[p].flag=true;
		tw[p].lm=tw[p].rm=tw[p].maxx=1;
		return ;
	}
	int mid=(l+r)>>1;
	build2(p<<1, l, mid);
	build2(p<<1|1, mid+1, r);
	update2(p);
}

inline void change2(int p, int x)
{
	if(tw[p].l==tw[p].r)
	{
		tw[p].flag=false;
		tw[p].lm=tw[p].rm=tw[p].maxx=0;
		return ;
	}
	int mid=(tw[p].l+tw[p].r)>>1;
	if(x<=mid)	change2(p<<1, x);
	else change2(p<<1|1, x);
	update2(p);
}

int main()
{
	w=read(), h=read(), Q=read();
	build1(1, 1, h-1);
	build2(1, 1, w-1);/**/
	while(Q--)
	{
		char op[2]; int x;
		scanf("%s%d", op, &x);
		if(op[0]=='H')	change1(1, x);
		else change2(1, x);
		ll ans=1ll*(th[1].maxx+1)*(tw[1].maxx+1);
		printf("%lld\n", ans);
	}
	return 0;
}

完……

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值