C. Glass Carving (CF Round #296 (Div. 2) STL--set的运用 && 并查集方法)
题意:一个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; }