D Cross Explosion||Toyota Programming Contest 2024#9(AtCoder Beginner Contest 370)
题意:给一个矩阵,矩阵元素数小于等于4e5。矩阵初始都是墙体。q次操作,q<=1e5。每次操作在坐标(r,c)处放置一个炸弹,若此处是墙体,则炸毁墙体,否则就造成十字爆炸伤害,炸毁以(r,c)为中心,十字方向第一次碰到的四个墙体(除了碰到边界)。
我一开始想的是线段树维护每行和每列的空隙情况,再二分判断炸毁的墙体位置。但是我不知道怎么开空间,而且感觉代码难度很大T_T
赛后翻了一下别人的代码,发现可以用vector<set<int> >v(n+1)来便捷维护行的情况(列的情况同理),豁然开朗,感觉自己STL还是用得很不熟练啊…> <
用vector<set<int> >v(n+1)来便捷维护行的情况(列的情况同理),以每一行为例,初始set中依次存入1到m,爆炸时1.如果该处有墙体则对应set.find()!=set.end()(行和列都要判断)的情况,然后set.erase()清除该处墙体。2.如果爆炸时该处没有墙体,则利用set.lower_bound()找出右侧第一个墙体,auto it=set.lower_bound();it--;则是左侧第一个墙体。(记得判断边界情况)。同样用set.erase()清除左右的墙体,但是记得清除墙体时同时要维护被清除墙体的列情况。(举例:设被清除墙体坐标为(x,y),那么该墙体被清除后,y列上的x同样需要被erase。我一开始就是忘了这里,还调了好一会儿。)初始答案为n*m,每清除一个墙体就ans--;最后就可以输出ans了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int h,w,q,r,c,ans; 4 void Work(int x,int y,vector<set<int> >&hang,vector<set<int> >&lie){ 5 if(hang[x].find(y)!=hang[x].end()&&lie[y].find(x)!=lie[y].end()){ 6 hang[x].erase(y); 7 lie[y].erase(x); 8 ans--; 9 return; 10 } 11 auto it=hang[x].lower_bound(y); 12 if(it!=hang[x].end()){ 13 hang[x].erase(it); 14 lie[*it].erase(x); 15 ans--; 16 } 17 it=hang[x].lower_bound(y); 18 if(it!=hang[x].begin()){ 19 it--; 20 hang[x].erase(it); 21 lie[*it].erase(x); 22 ans--; 23 } 24 it=lie[y].lower_bound(x); 25 if(it!=lie[y].end()){ 26 lie[y].erase(it); 27 hang[*it].erase(y); 28 ans--; 29 } 30 it=lie[y].lower_bound(x); 31 if(it!=lie[y].begin()){ 32 it--; 33 lie[y].erase(it); 34 hang[*it].erase(y); 35 ans--; 36 } 37 return; 38 } 39 void init(){ 40 scanf("%d%d%d",&h,&w,&q); 41 vector<set<int> >hang(h+1); 42 vector<set<int> >lie(w+1); 43 ans=h*w; 44 for(int i=1;i<=h;i++) 45 for(int j=1;j<=w;j++){ 46 hang[i].insert(j); 47 lie[j].insert(i); 48 } 49 for(int i=1;i<=q;i++){ 50 scanf("%d%d",&r,&c); 51 Work(r,c,hang,lie); 52 } 53 printf("%d\n",ans); 54 return; 55 } 56 int main(){ 57 init(); 58 return 0; 59 }