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;