题意:一个w*h的玻璃,现在水平或竖直切n次(“H”表示水平切,“V”表示竖直切),每一次切后输出当前切成的块中的最大面积。
思路:用set记录切割的位置(要用两个set,分别来记录长和宽),multiset记录某一条边被切后 所得到的 小段的长度(也要两个,分别记录长和宽的)。那么每次切后就从multiset中取出最大的长和宽,相乘即得面积。
STL set 写法
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
#include <stack>
#include <vector>
#include <set>
#include <queue>
#define FRE(i,a,b) for(i = a; i <= b; i++)
#define FRL(i,a,b) for(i = a; i < b; i++)
#define mem(t, v) memset ((t) , v, sizeof(t))
#define sf(n) scanf("%d", &n)
#define sff(a,b) scanf("%d %d", &a, &b)
#define sfff(a,b,c) scanf("%d %d %d", &a, &b, &c)
#define pf printf
#define DBG pf("Hi\n")
typedef __int64 ll;
using namespace std;
int w,h,n;
set<int>wxs;
set<int>hxs;
multiset<int>wds;
multiset<int>hds;
int main()
{
int i,j;
while (~sfff(w,h,n))
{
set<int>::iterator it,p;
char s[3];
int x;
wxs.clear();
hxs.clear();
wds.clear();
hds.clear();
wxs.insert(0); wxs.insert(w);
hxs.insert(0); hxs.insert(h);
wds.insert(w); hds.insert(h);
while (n--)
{
scanf("%s%d",s,&x);
if (s[0]=='H')
{
it=hxs.lower_bound(x);
p=it;
p--;
int dis = *it - *p;
hds.erase(hds.find(dis));//这里不能写成hds.erase(dis),在multiset里面这样写会把所有值等于dis的点删掉,这显然不符合我们的题意
hds.insert(*it-x);
hds.insert(x-*p);
hxs.insert(x);
}
else
{
it=wxs.lower_bound(x);
p=it;
p--;
int dis = *it - *p;
wds.erase(wds.find(dis));
wds.insert(*it-x);
wds.insert(x-*p);
wxs.insert(x);
}
int xx= *wds.rbegin();
int yy= *hds.rbegin();
pf("%I64d\n",(ll)xx * (ll)yy); //最后要强制转化,不然会爆int
}
}
return 0;
}
并查集写法
思路:并查集初始化,首先将玻璃全部切成1*1的小块,然后先保存下所有的操作,记录下它切了哪些位置(数组vis_w和vis_h),接着将没有被切的位置 i 连起来Union(i,i+1),最后倒着把要切的位置连起来,这个过程中记录每次两条边的最大值,它们的乘积保存下来就是答案。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
#include <stack>
#include <vector>
#include <set>
#include <queue>
#define maxn 200010
#define FRE(i,a,b) for(i = a; i <= b; i++)
#define FRL(i,a,b) for(i = a; i < b; i++)
#define mem(t, v) memset ((t) , v, sizeof(t))
#define sf(n) scanf("%d", &n)
#define sff(a,b) scanf("%d %d", &a, &b)
#define sfff(a,b,c) scanf("%d %d %d", &a, &b, &c)
#define pf printf
#define DBG pf("Hi\n")
typedef __int64 ll;
using namespace std;
//维护两个并查集,分别维护w和h
int w,h,n,max_w,max_h;
int father_w[maxn],father_h[maxn];
int num_w[maxn],num_h[maxn];
bool vis_w[maxn],vis_h[maxn];
char s[maxn][3];
int pos[maxn];
ll ans[maxn];
void init()
{
int i;
FRL(i,0,maxn)
{
father_w[i]=i;
father_h[i]=i;
num_w[i]=1;
num_h[i]=1;
}
num_w[0]=0;
num_h[0]=0;
mem(vis_w,false);
mem(vis_h,false);
max_w=1;//用来记录每次Union操作后边的最大值
max_h=1;
}
int find_father_w(int x)
{
if (x!=father_w[x])
father_w[x]=find_father_w(father_w[x]);
return father_w[x];
}
int find_father_h(int x)
{
if (x!=father_h[x])
father_h[x]=find_father_h(father_h[x]);
return father_h[x];
}
void Union_w(int a,int b)
{
int fa=find_father_w(a);
int fb=find_father_w(b);
if (fa!=fb)
{
father_w[fb]=fa;
num_w[fa]+=num_w[fb];
}
max_w=max(max_w,num_w[fa]);
}
void Union_h(int a,int b)
{
int fa=find_father_h(a);
int fb=find_father_h(b);
if (fa!=fb)
{
father_h[fb]=fa;
num_h[fa]+=num_h[fb];
}
max_h=max(max_h,num_h[fa]);
}
int main()
{
int i,j;
while (~sfff(w,h,n))
{
init();
FRE(i,1,n)
{
scanf("%s%d",s[i],&pos[i]);
if (s[i][0]=='H') vis_h[pos[i]]=true;
else vis_w[pos[i]]=true;
}
FRL(i,1,w)//先把没有被切的位置连起来
{
if (!vis_w[i])
Union_w(i,i+1);
}
FRL(i,1,h)
{
if (!vis_h[i])
Union_h(i,i+1);
}
for (i=n;i>0;i--)//逆操作连接
{
// pf("max_w=%d\nmax_h=%d\n***\n",max_w,max_h);
ans[i]=(ll)max_w * (ll)max_h;//保存答案
if (s[i][0]=='H') Union_h(pos[i],pos[i]+1);
else Union_w(pos[i],pos[i]+1);
}
FRE(i,1,n)
pf("%I64d\n",ans[i]);
}
return 0;
}