ABC218E Akari 题解
年代久远
您正在查看一篇年代久远的题解,由于 Markdown 格式不兼容等问题,页面渲染可能会出现问题。其中的时效性信息可能不再有效,请仔细辨别。
题面(洛谷)
题面(AtCoder)
思路
拿到这题的第一个想法是打暴力模拟,对每个灯泡都上下左右扫一遍,直至障碍物或者边界,最后得出答案。但时间复杂度是 \(O((H+W)\cdot N)\) 级别的,一定会 TLE。
考虑一个简单的优化,既然灯泡只能向上下左右发出光亮,那干脆沿着灯泡发出光亮的方向扫就可以了。
一次扫一个方向,四次扫四个方向,遇到灯泡就准备标记,遇到障碍物就准备停止标记,很好地避免了单独计算灯泡浪费时间的问题。如果不考虑读入,时间复杂度 \(O(HW)\)。
代码
| // Coder : Hellolin
// Time : 2023-01-25 19:40:17
// Problem : [ABC182E] Akari
// Contest : Luogu
// URL : https://www.luogu.com.cn/problem/AT_abc182_e
// Time Limit : 2500 ms
// Memory Limit : 1 GiB
#include <iostream>
const int MAX=1900;
// h, w, n 和 m 意义如题,p 数组存放网格图,x 和 y 用作读入灯泡/障碍物坐标
int h, w, n, m, p[MAX][MAX], x, y, ans;
// z 数组存放每个方格灯光照射情况
bool z[MAX][MAX], f;
int main()
{
// 加速输入输出
std::ios::sync_with_stdio(false);
std::cin>>h>>w>>n>>m;
for(int i=1; i<=n; i++)
{
std::cin>>x>>y;
p[x][y]=1;
}
for(int i=1; i<=m; i++)
{
std::cin>>x>>y;
p[x][y]=-1;
}
// 对于每一行,向右照射灯光
for(int i=1; i<=h; i++)
{
f=0; // 初始没有光
for(int j=1; j<=w; j++)
{
if(p[i][j]==1) f=1; // 有灯泡
else if(p[i][j]==-1) f=0; // 有障碍物
if(f&&(!z[i][j])) // 可以被光照到但未标记
{
z[i][j]=1; // 标记
++ans;
}
}
}
// 对于每一行,向左照射灯光
for(int i=1; i<=h; i++)
{
f=0;
for(int j=w; j>=1; j--)
{
if(p[i][j]==1) f=1;
else if(p[i][j]==-1) f=0;
if(f&&(!z[i][j]))
{
z[i][j]=1;
++ans;
}
}
}
// 对于每一列,向下照射灯光
for(int j=w; j>=1; j--)
{
f=0;
for(int i=1; i<=h; i++)
{
if(p[i][j]==1) f=1;
else if(p[i][j]==-1) f=0;
if(f&&(!z[i][j]))
{
z[i][j]=1;
++ans;
}
}
}
// 对于每一列,向上照射灯光
for(int j=w; j>=1; j--)
{
f=0;
for(int i=h; i>=1; i--)
{
if(p[i][j]==1) f=1;
else if(p[i][j]==-1) f=0;
if(f&&(!z[i][j]))
{
z[i][j]=1;
++ans;
}
}
}
// 输出答案
std::cout<<ans<<std::endl;
return 0;
}
|