题目传送门


题目描述

有一个$n\times m$的格子图,其中有一些是黑色的,另一些为白色。
从某个白色格子的中心点向左上($NW$),左下($SW$),右上($NE$),右下($SE$)四个方向中的一个发出一束光线,若光线碰到黑色格子或者墙壁(即边界)会反射。反射情况如图所示:

我们不难发现,光线能穿过的格子总数是可以算出的。假如光线经过了某个格子的中心,则称光线经过了这个格子。求光线经过的格子总数。
由于答案可能很大,请使用$long\ long$的$C++$选手注意:请勿使用$\%lld$,推荐$cout$或者$\%I64d$


输入格式

第一行三个整数$n$、$m$、$k$,接下来$k$行每行两个整数$x_i$和$y_i$,表示第$i$个堵塞的格子的坐标。
最后一行两个整数$x_s,y_s$和一个字符串表示激光发出的方向。$“NE”,“NW”,“SE”,“SW”$分别表示方向$(-1,1),(-1,-1),(1,1),(1,-1)$。
保证输入的堵塞的格子坐标不重复。


输出格式

一行输出光束至少通过一次的空格子数。


样例

样例输入

7 5 3
3 3
4 3
5 3
2 1 SE

样例输出

14


数据范围与提示

$30\%$的数据,$n,m \leqslant 30$。

$60\%$的数据,$n,m \leqslant 1000$。

另$20\%$的数据,$k=0$。

$100\%$的数据,$n,m,k \leqslant 100,000$


题解

$30\%$算法:

我觉得这是这道题的难点所在,因为我到现在还想不出来$30\%$的算法怎么打。

$60\%$算法:

稍加思考,发现如果重复经过了某种状态,肯定已经循环过了一次。

想一想,一共有$n^2$个格子,$4$个方向,那么就有$4 \times n^2$个状态,暴力进去搜就好了。

不过需要注意以下几点:

  $1.$只有当当前状态被访问过了才可以跳出,并不是当前格子被访问过了。

  $2.$如果当前状态没有被访问过,但是当前格子被访问过了,答案不变。

时间复杂度:$O(4\times n^2)$。

期望得分:$60$分。

实际得分:$60$分。

另$20\%$算法:

考虑$k=0$,意思就是说,中间没有任何堵塞的格子,那么分为一下三种情况:

  $1.$如果$n \neq m$,那么不管从那个点往哪个方向出发,都一定把整张图全跑一遍,答案即为$n \times m$,记得开$long long$就好了。

  $2.$如果$n=m$,光线沿对角线发射,那么它会射到对角,再反弹回来,答案即为$n$。

  $3.$还是$n=m$,但是光线不沿对角线发射,那么它会转一圈,答案即为$2 \times n$。

期望多得分数:$10$分。

$100\%$算法:

  看一眼那$10^5$的数据范围,直接存图显然是不能接受的,那么我们这么考虑这个问题,用一个set存图,然后每次用二分法计算出下一个转向的位置,将答案加上,转向次数是$O(n+m+k)$级别的,每次查询是$O(\log k)$级别的,那么我们的理论最劣时间复杂度为:$O((n+m+k)\log k)$。

  根据题目中光线反射的方式,可以发现,每当光线沿$NW$、$SE$方向前进时,只会经过一种颜色的网格,每当光线沿$NE$、$SW$方向前进时,只会经过另一种颜色的网格。所以光线在某一个格子中心时,要么只会是$NW$、$SE$方向之一,要么只会是$NE$、$SW$方向之一,所以一个点最多只会被经过两次。

  易知,如果光线在前进过程中出现过如下两种反射,所有格子就会被经过两次。

  

  只需在模拟的过程中记录是否出现过这两种情况即可。


代码时刻

$60\%$算法:

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
int ans;
bool Map[1001][1001];//记录堵塞
bool vis[1001][1001][5];//记录状态
void dfs(int x,int y,int w)
{
if(vis[x][y][w])//出现了已经访问过的状态
{
printf("%d",ans);
exit(0);
}
if(!vis[x][y][1]&&!vis[x][y][2]&&!vis[x][y][3]&&!vis[x][y][4])ans++;//如果这个点还没有被经过过
vis[x][y][w]=1;//标记这种状态已经出现过
if(w==1)//四个方向
{
if(!Map[x-1][y+1]){dfs(x-1,y+1,1);return;}//四种情况,小心别打错就好了
if((Map[x-1][y]&&Map[x][y+1])||(!Map[x-1][y]&&!Map[x][y+1])){dfs(x,y,4);return;}
if(Map[x-1][y]){dfs(x,y+1,3);return;}
if(Map[x][y+1]){dfs(x-1,y,2);return;}
}
if(w==2)
{
if(!Map[x-1][y-1]){dfs(x-1,y-1,2);return;}
if((Map[x][y-1]&&Map[x-1][y])||(!Map[x][y-1]&&!Map[x-1][y])){dfs(x,y,3);return;}
if(Map[x][y-1]){dfs(x-1,y,1);return;}
if(Map[x-1][y]){dfs(x,y-1,4);return;}
}
if(w==3)
{
if(!Map[x+1][y+1]){dfs(x+1,y+1,3);return;}
if((Map[x+1][y]&&Map[x][y+1])||(!Map[x+1][y]&&!Map[x][y+1])){dfs(x,y,2);return;}
if(Map[x+1][y]){dfs(x,y+1,1);return;}
if(Map[x][y+1]){dfs(x+1,y,4);return;}
}
if(w==4)
{
if(!Map[x+1][y-1]){dfs(x+1,y-1,4);return;}
if((Map[x+1][y]&&Map[x][y-1])||(!Map[x+1][y]&&!Map[x][y-1])){dfs(x,y,1);return;}
if(Map[x+1][y]){dfs(x,y-1,2);return;}
if(Map[x][y-1]){dfs(x+1,y,3);return;}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=k;i++)
{
int x,y;
scanf("%d%d",&x,&y);
Map[x][y]=1;
}
int x,y;
char ch[5];//这个稍微开打点,因为输入最后会有'\0'。
scanf("%d%d%s",&x,&y,ch+1);
for(int i=0;i<=n+1;i++)Map[i][0]=Map[i][m+1]=1;//边界
for(int i=0;i<=m+1;i++)Map[0][i]=Map[n+1][i]=1;
if(ch[1]=='N'&&ch[2]=='E')dfs(x,y,1);
if(ch[1]=='N'&&ch[2]=='W')dfs(x,y,2);
if(ch[1]=='S'&&ch[2]=='E')dfs(x,y,3);
if(ch[1]=='S'&&ch[2]=='W')dfs(x,y,4);
return 0;
}

$100\%$算法:

#include<bits/stdc++.h>
using namespace std;
struct rec{int x,y,d;};
int n,m,k;
long long ans;//注意long long
set<int>s1[200010],s2[200010];
map<pair<int,int>,bool>mp;
int getid(int x,int y,int d){return d==1?x-y+m+1:x+y;}
bool same(rec a,rec b){if(a.x==b.x&&a.y==b.y&&a.d==b.d)return 1;return 0;}
bool check(int x,int y){return mp[make_pair(x,y)];}
void add(int x,int y)//标记堵塞
{
s1[getid(x,y,1)].insert(x);
s2[getid(x,y,2)].insert(x);
mp[make_pair(x,y)]=1;
}
pair<rec,int> dfs(rec u)//暴力搜索
{
rec re;
set<int>::iterator it;
if(u.d==1)//四个方向
{
it=s1[getid(u.x,u.y,1)].lower_bound(u.x);it--;//用set和lower_bound在log的时间复杂度内求出答案
re.x=u.x-(abs(*it-u.x)-1);
re.y=u.y-(abs(*it-u.x)-1);
if(check(re.x-1,re.y)&&check(re.x,re.y-1))re.d=3;//枚举情况
else if(check(re.x-1,re.y)){re.y--;re.d=4;}
else if(check(re.x,re.y-1)){re.x--;re.d=2;}
else re.d=3;
}
if(u.d==2)
{
it=s2[getid(u.x,u.y,2)].lower_bound(u.x);it--;
re.x=u.x-(abs(*it-u.x)-1);
re.y=u.y+(abs(*it-u.x)-1);
if(check(re.x-1,re.y)&&check(re.x,re.y+1))re.d=4;
else if(check(re.x-1,re.y)){re.y++;re.d=3;}
else if(check(re.x,re.y+1)){re.x--;re.d=1;}
else re.d=4;
}
if(u.d==3)
{
it=s1[getid(u.x,u.y,1)].lower_bound(u.x);
re.x=u.x+(abs(*it-u.x)-1);
re.y=u.y+(abs(*it-u.x)-1);
if(check(re.x+1,re.y)&&check(re.x,re.y+1))re.d=1;
else if(check(re.x+1,re.y)){re.y++;re.d=2;}
else if(check(re.x,re.y+1)){re.x++;re.d=4;}
else re.d=1;
}
if(u.d==4)
{
it=s2[getid(u.x,u.y,2)].lower_bound(u.x);
re.x=u.x+(abs(*it-u.x)-1);
re.y=u.y-(abs(*it-u.x)-1);
if(check(re.x+1,re.y)&&check(re.x,re.y-1))re.d=2;
else if(check(re.x+1,re.y)){re.y--;re.d=1;}
else if(check(re.x,re.y-1)){re.x++;re.d=3;}
else re.d=2;
}
return make_pair(re,abs(*it-u.x));
}
bool judge(rec u)
{
rec re=u;
do
{
pair<rec,int> cur=dfs(u);
ans+=(long long)cur.second;
switch(cur.first.d)//转向
{
case 1:if(u.d==3)return 0;break;
case 2:if(u.d==4)return 0;break;
case 3:if(u.d==1)return 0;break;
case 4:if(u.d==2)return 0;break;
}
u=cur.first;
}while(!same(re,u));
return 1;
}
void pre_build()//处理边界
{
for(int i=0;i<=m+1;i++)
{
add(0,i);
add(n+1,i);
}
for(int i=1;i<=n;i++)
{
add(i,0);
add(i,m+1);
}
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
pre_build();
for(int i=1;i<=k;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
}
int x,y,d;
char ch[5];
scanf("%d%d%s",&x,&y,ch+1);
if(ch[1]=='N'&&ch[2]=='W')d=1;
if(ch[1]=='N'&&ch[2]=='E')d=2;
if(ch[1]=='S'&&ch[2]=='E')d=3;
if(ch[1]=='S'&&ch[2]=='W')d=4;
rec st={x,y,d};
st=dfs(st).first;
if(!judge(st))//开始模拟
{
ans--;
switch(st.d)
{
case 1:st.d=3;break;
case 2:st.d=4;break;
case 3:st.d=1;break;
case 4:st.d=2;break;
}
judge(st);
}
printf("%lld",ans);
return 0;
}
 

rp++

[Codeforces 274E]:Mirror Room(模拟)的更多相关文章

  1. [CodeForces] 274E Mirror Room

    题意翻译 有一个n*m的格子图,其中有一些是黑色的,另一些为白色. 从某个白色格子的中心点向左上(NW),左下(SW),右上(NE),右下(SE)四个方向中的一个发出一束光线,若光线碰到黑色格子或者墙 ...

  2. Codeforces 738D. Sea Battle 模拟

    D. Sea Battle time limit per test: 1 second memory limit per test :256 megabytes input: standard inp ...

  3. Codeforces 626A Robot Sequence(模拟)

    A. Robot Sequence time limit per test:2 seconds memory limit per test:256 megabytes input:standard i ...

  4. CodeForces - 589D(暴力+模拟)

    题目链接:http://codeforces.com/problemset/problem/589/D 题目大意:给出n个人行走的开始时刻,开始时间和结束时间,求每个人分别能跟多少人相遇打招呼(每两人 ...

  5. Codeforces 767B. The Queue 模拟题

    B. The Queue time limit per test:1 second memory limit per test:256 megabytes input:standard input o ...

  6. Codeforces 704A Thor 队列模拟

    题目大意:托尔有一部手机可执行三种操作 1.x APP产生一个新消息 2.读取x App已产生的所有消息 3.读取前t个产生的消息 问每次操作后未读取的消息的数量 题目思路: 队列模拟,坑点在于竟然卡 ...

  7. Vasya And The Mushrooms CodeForces - 1016C (前缀和模拟)

    大意: 给定2*n的矩阵, 每个格子有权值, 走到一个格子的贡献为之前走的步数*权值, 每个格子只能走一次, 求走完所有格子最大贡献. 沙茶模拟打了一个小时总算打出来了 #include <io ...

  8. Codeforces 691C. Exponential notation 模拟题

    C. Exponential notation time limit per test: 2 seconds memory limit per test:256 megabytes input: st ...

  9. Codeforces 658A. Robbers' watch 模拟

    A. Robbers' watch time limit per test: 2 seconds memory limit per test: 256 megabytes input: standar ...

随机推荐

  1. Redis的消息订阅及发布及事务机制

    Redis的消息订阅及发布及事务机制 订阅发布 SUBSCRIBE PUBLISH 订阅消息队列及发布消息. # 首先要打开redis-cli shell窗口 一个用于消息发布 一个用于消息订阅 # ...

  2. java常用类详细介绍及总结:字符串相关类、日期时间API、比较器接口、System、Math、BigInteger与BigDecimal

    一.字符串相关的类 1.String及常用方法 1.1 String的特性 String:字符串,使用一对""引起来表示. String声明为final的,不可被继承 String ...

  3. 二、JVM — 垃圾回收

    JVM 垃圾回收 写在前面 本节常见面试题 本文导火索 1 揭开 JVM 内存分配与回收的神秘面纱 1.1 对象优先在 eden 区分配 1.2 大对象直接进入老年代 1.3 长期存活的对象将进入老年 ...

  4. babel的初步了解

    前段时间开始研究ast,然后慢慢的顺便把babel都研究了,至于ast稍后的时间会写一篇介绍性博客专门介绍ast,本博客先介绍一下babel的基本知识点. 背景: 由于现在前端出现了很多非es5的语法 ...

  5. C++的同名属性(没有虚拟属性)、同名普通函数、同名静态函数(没有虚拟静态函数),是否被覆盖

    例子1:属性的覆盖#include "stdafx.h" class A {public: int i; A() { i=1; }}; class B: public A {pub ...

  6. django学习笔记(四)

    1.请求周期 url> 路由 > 函数或类 > 返回字符串或者模板语言? Form表单提交: 提交 -> url > 函数或类中的方法 - .... HttpRespon ...

  7. C#选择文件返回缩略图

    传入文件路径,返回临时文件中缩略图的路径,jpg,pdf,office,rar都行 string path = ThumbnailHelper.GetInstance().GetJPGThumbnai ...

  8. mysql两种备份方法总结:mysqldump 和 xtrabackup

    mysqldump工具基本使用 1. mysqldump [OPTIONS] database [tables…] 还原时库必须存在,不存在需要手动创建     --all-databases: 备份 ...

  9. linux 打包和压缩的概念和区别

    对于刚刚接触Linux的人来说,一定会给Linux下一大堆各式各样的文件名 给搞晕.别个不说,单单就压缩文件为例,我们知道在Windows下最常见的压缩文件就只有两种,一是,zip,另一个是.rar. ...

  10. Mysql逻辑架构介绍

    总体概览: 和其它数据库相比,MySQL有点与众不同,它的架构可以在多种不同场景中应用并发挥良好作用.主要体现在存储引擎的架构上,插件式的存储引擎架构将查询处理和其它的系统任务以及数据的存储提取相分离 ...