1562 玻璃切割 (线段树最长连续元素“1”个数 or 逆向模拟过程题型+链表)

1562 玻璃切割

现在有一块玻璃,是长方形的(w 毫米× h 毫米),现在要对他进行切割。

切割的方向有两种,横向和纵向。每一次切割之后就会有若干块玻璃被分成两块

更小的玻璃。在切割之后玻璃不会被移动。现在想知道每次切割之后面积最大的

一块玻璃是多少。

样例解释:

对于第四次切割,下面四块玻璃的面积是一样大的。都是2。

输入

单组测试数据。
第一行有三个整数 w,h,n (2≤w,h≤200000, 1≤n≤200000),表示玻璃在横向上长w 毫米,
纵向上长h 毫米,接下来有n次的切割。
接下来有n行输入,每一行描述一次切割。
输入的格式是H y 或 V x。
H y表示横向切割,切割线距离下边缘y毫米(1≤y≤h-1)。
V x表示纵向切割,切割线距离左边缘x毫米(1≤x≤w-1)。
输入保证不会有两次切割是一样的。

输出

对于每一次切割,输出所有玻璃中面积最大的是多少。

输入样例

4 3 4
H 2
V 2
V 3
V 1

输出样例

8
4
4
2

思路描述:

           首先,不难发现,题目即求每次变化之后最大的长和最大的高相乘即当前答案。

           如果正向考虑,假设当前状态高度最大值maxh,长度同理,插入一条新的切割线之后,

原来最大值可能被破坏,且新的最大高度不好确定,除非遍历所有长度。

          而如果逆向考虑,去掉一条切割线后,只需要将原来的最大高度与新合并产生的高度比

较即可得出新的最大高度。

         所以我们只要将竖着的边和横着的边分别构建链表即可。因为我们要通过N次询问来

定位相应的边在链表中的位置,所以,链表的下标直接用坐标表示,坐标最大2e5,

不会爆数组。

代码实现 O(N):

#include<iostream>
#include<cstring>
#include<cmath>
#include<stack>
#include<algorithm>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
const int N=2e5+100;
struct edgel {    //横边 
    int Left,Right;//左右邻接边 
} el[N];
struct edgeh {  //竖边 
    int up,down;//上下邻接边 
} eh[N];
struct query { //询问 
    char ch;
    int xx,id;//id:查询序号 
	LL ans;//每次询问的答案 
} arr[N];
bool cmp1(query aa,query bb) {
    return aa.xx<bb.xx;
}
bool cmp2(query aa,query bb){
	return aa.id<bb.id;
}
int main() {

    int w,h,n,preh,prel;//pre构建链表时的临时量 
    int maxsh,maxsl;//最大长度、高度 
    while(cin>>w>>h>>n) {
        maxsh=maxsl=0;
        for(int i=1; i<=n; i++) {
            cin>>arr[i].ch>>arr[i].xx;
            arr[i].id=i;
        }
        sort(arr+1,arr+1+n,cmp1);  //按照坐标从小到大,方便构建链表 

        preh=prel=0;
        for(int i=1; i<=n; i++) {

            if(arr[i].ch=='V') {
                el[arr[i].xx].Left=prel;
                maxsl=max(maxsl,-prel+arr[i].xx);//构建链表,顺便求出最终的最大值 
                el[prel].Right=arr[i].xx;
                prel=arr[i].xx;
            } else {
                eh[arr[i].xx].down=preh;
                maxsh=max(maxsh,-preh+arr[i].xx);
                eh[preh].up=arr[i].xx;
                preh=arr[i].xx;
            }
        }
        
        el[w].Left=prel;  //头尾处理 
        el[prel].Right=w;
        eh[h].down=preh;
        eh[preh].up=h;
        maxsl=max(maxsl,w-prel);
        maxsh=max(maxsh,h-preh);
        
		sort(arr+1,arr+1+n,cmp2);//按照查询顺序排回去 
        arr[n].ans=(LL)maxsl*(LL)maxsh;
        
		for(int i=n;i>=1;i--){
			
			//删边并更新链表结构和最大值 
			if( arr[i].ch=='V' ){
				el[el[arr[i].xx].Left].Right=el[arr[i].xx].Right;
				el[el[arr[i].xx].Right].Left=el[arr[i].xx].Left;
				maxsl=max(maxsl,el[arr[i].xx].Right-el[arr[i].xx].Left);
				
			}else{
				
				eh[eh[arr[i].xx].down].up=eh[arr[i].xx].up;
				eh[eh[arr[i].xx].up].down=eh[arr[i].xx].down;
				maxsh=max(maxsh,eh[arr[i].xx].up-eh[arr[i].xx].down);
				
			}
		//记录相应状态答案 
			arr[i-1].ans=(LL)maxsh*(LL)maxsl;
		}
		//按查询顺序输出答案 
		for(int i=1;i<=n;i++){
			cout<<arr[i].ans<<endl;
		}
    }

    return 0;
}

两棵线段树做法:

         为长和高分别开一棵线段树,每次查询即线段树上单点修改为 0 并查询整棵线段树最大连续的 1 的个数,

(矩形坐标0~h/0~w,线段树在1~h-1/1~w-1上建立。本题巧妙的地方在于查询出来的点数加一正好等于对应线

段长度)注意这一题型的代码实现和最大连续区间和还是有一定差别的。

代码实现:

#include<iostream>
#include<cstring>
#include<cmath>
#include<stack>
#include<algorithm>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
const int N=4*2e5+100;
struct SegmentTree {
    int l,r;
    int dat;
    int lmax,rmax;
} tl[N],th[N];
int max(int x,int y,int z) {
    if(x>y) {
        if(x>z)return x;
        else return z;
    } else {
        if(y>z)return y;
        else return z;
    }
}
int max(int x,int y) {
    if(x>y)return x;
    return y;
}
void build(int p,int l,int r,SegmentTree *t) { //t指某棵线段树
    t[p].l=l;
    t[p].r=r;
    if(l==r) {
        t[p].dat=1;
        t[p].lmax=t[p].rmax=1;
        return ;
    }
    int mid=(l+r)/2;
    build(p*2,l,mid,t);
    build(p*2+1,mid+1,r,t);

    t[p].lmax=t[p*2].lmax;
    if(t[p*2].lmax==t[p*2].r-t[p*2].l+1){
    	t[p].lmax+=t[p*2+1].lmax;
	}
    t[p].rmax=t[p*2+1].rmax;
    if(t[p*2+1].rmax==t[p*2+1].r-t[p*2+1].l+1){
    	t[p].rmax+=t[p*2].rmax;
	}
	
    t[p].dat=max(t[p].lmax,t[p].rmax,t[2*p].rmax+t[p*2+1].lmax);
    t[p].dat=max(t[p].dat,t[p*2].dat,t[p*2+1].dat);//最大值来源有3个
}
void change(int p,int x,int v,SegmentTree *t) {
    if(t[p].l==t[p].r) {
        t[p].dat=v;
        t[p].lmax=t[p].rmax=v;
        return ;
    }
    int mid=(t[p].l+t[p].r)/2;
    if(x<=mid)change(p*2,x,v,t);
    else change(p*2+1,x,v,t);

	t[p].lmax=t[p*2].lmax;
    if(t[p*2].lmax==t[p*2].r-t[p*2].l+1){
    	t[p].lmax+=t[p*2+1].lmax;
	}
    t[p].rmax=t[p*2+1].rmax;
    if(t[p*2+1].rmax==t[p*2+1].r-t[p*2+1].l+1){
    	t[p].rmax+=t[p*2].rmax;
	}
    t[p].dat=max(t[p].lmax,t[p].rmax,t[2*p].rmax+t[p*2+1].lmax);
    t[p].dat=max(t[p].dat,t[p*2].dat,t[p*2+1].dat);
}
int main() {
    
    int w,h,n,x;
    char ch;
    int maxsh,maxsl;
    LL ans;
    while(cin>>w>>h>>n){
    	maxsh=maxsl=0;
    	build(1,1,h-1,th);
    	build(1,1,w-1,tl);
    	for(int i=1;i<=n;i++){
    		cin>>ch>>x;
    		if(ch=='V'){
    			change(1,x,0,tl);
			}else{
				change(1,x,0,th);
			}
			maxsh=th[1].dat;
			maxsl=tl[1].dat;
			ans=(LL)(maxsh+1)*(LL)(maxsl+1);
			cout<<ans<<endl;
		}
	}
	
	return 0;
    
}

THE END;

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值