10月17日备战Noip2018模拟赛15(A组)
T3时钟淘淘起床了
题目描述
今天就是IOI啦,好开心!
可是淘淘还在睡觉啊,要把他叫醒来!
淘淘在家睡觉都是分身的,他会分身成ñ个人,然后在家里找个地方朝四面墙中任意一面躺下睡觉!
要想叫醒淘淘,必须让每个分身都听到闹钟!
但是啊,闹钟太多了会导致声音过大把淘淘震傻导致不会写FWT和FFT,那么他就无法akioi啦!
在二维平面上以(0,0)为左下角,(W,d)为右上角的矩形区域内有一些点,每个点代表一个人,每个人将会面对一个方向(东南西北) ,一个人的视野范围是以该点为顶点的直角,角平分线与其所面对的方向平行,角的两条边会与矩形交于两点,矩形上两点之间的部分,为该人的可视部分。为了让每个人都能知道时间,你需要在矩形的边界上放置一些时钟(视为一个点),使得每个人的可视部分内都至少有一个时钟,求最少需要放置几个时钟。
输入格式
第一行三个整数,N,W,d;分别代表人数,以及矩形的右上角所在点的坐标。
接下来Ñ行每行两个整数和一个大写字母,表示该人的坐标,以及面对的方向
输出格式
输出一行,一个整数,代表最少需要放置几个时钟。
输入样例
2 10 6
4 4 E
6 4 W
输出样例
2
样例解释
如图一
数据范围
对于10%的数据:n≤5,w≤5,d≤5。
对于20%的数据:n≤100,w≤100,d≤100。
对于40%的数据:n≤1000,w≤5000,d≤5000。
对于另外10%的数据:n≤1000,w = 2,d≤105。
对于70%的数据:n≤1000,w≤105,d≤105。
对于100%的数据:n≤5000,w≤105,d≤105。
保证人的坐标不在边界上,但不保证坐标不重复。
思路
模拟+贪心
这道题真的超级考恶心的细节啊
先把问题简单化,将每个分身的视野范围对应到区间上,然后这个问题就变成了,在一个环上有许多区间,求最大不相交区间数,说起来很简单,但是对于如何将视野对应到区间上还是要仔细一些,具体方法看后面;
在线段上求最大不相交区间数是很常见的,如果到了环上,可以把每个区间作为开始的第一个区间枚举,再使用贪心求解,最后取最大值,复杂度
再来说一些细节部分
coordinate
首先要对环进行标号,也就是自定义函数coordinate的作用,lft [],dwn [],rgt [],upp []分别记录,左下右上四边的坐标的坐标
inline void coordinate ()
{
for (int i = d; i >= 0; -- i){
lft[i] = ++ co;
}
for (int i = 1; i < w; ++ i){
dwn[i] = ++ co;
}
for (int i = 0; i <= d; ++ i){
rgt[i] = ++ co;
}
for (int i = w - 1; i >= 1; -- i){
upp[i] = ++ co;
}
return ;
}
那举个栗子说明一下,比如一个w = 4,d = 3,的矩形,标记完坐标后是这样的
mark
mark函数用于把视野范围转换为区间
u = w - x;
v = d - y;
if (ch == 'N'){
L = u + y <= d ? rgt[u + y] : upp[v + x];
R = x + y <= d ? lft[x + y] + C : upp[x - v];
a[cnt] = Interval (L, R);
return ;
}
就用面朝北方的来举例子
如果一个人的视野是这样的
以右端点R为例,若,说明R在lft []上,如上图所示,反之则在upp []上,这里可以利用视角是45°的等腰直角三角形性质来进行转化,其他方向也是类似的
代码
#include <iostream>
#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
const int MAXN = 5e3 + 5;
const int MAXE = 1e5 + 5;
int n, w, d, co, C, tot;
int lft[MAXE], dwn[MAXE], rgt[MAXE], upp[MAXE];
struct Interval{
int l, r;
Interval(){ }
Interval (int _l, int _r) : l (_l), r (_r) { }
bool operator < (const Interval &rhs) const
{
return r < rhs.r || r == rhs.r && l < rhs.l;
}
}a[MAXN];
inline int readint ();
inline char readchar ();
inline void coordinate ();
inline void mark (int x, int y, char ch, int cnt);
inline void count ();
int main ()
{
freopen ("clock.in", "r", stdin);
freopen ("clock.out", "w", stdout);
int x, y;
char ch;
n = readint (), w = readint (), d = readint ();
coordinate ();
C = (w + d) * 2; //C是周长
for (int i = 1; i <= n; ++ i){
x = readint (), y = readint (), ch = readchar ();
mark (x, y, ch, i);
}
sort (a + 1, a + 1 + n); //按照右端点排序
count (); //贪心求解
printf ("%d", tot);
fclose (stdin);
fclose (stdout);
return 0;
}
inline int readint ()
{
char ch = getchar ();
while (!isdigit (ch)) ch = getchar ();
int x = 0;
while (isdigit (ch)){
x = x * 10 + ch - '0';
ch = getchar ();
}
return x;
}
inline char readchar ()
{
char ch;
while (ch = getchar (), ch <'A' || ch > 'Z');
return ch;
}
inline void coordinate ()
{
for (int i = d; i >= 0; -- i){
lft[i] = ++ co;
}
for (int i = 1; i < w; ++ i){
dwn[i] = ++ co;
}
for (int i = 0; i <= d; ++ i){
rgt[i] = ++ co;
}
for (int i = w - 1; i >= 1; -- i){
upp[i] = ++ co;
}
return ;
}
inline void mark (int x, int y, char ch, int cnt) //将视野范围转化为区间
{
int u, v, L, R;
u = w - x;
v = d - y;
if (ch == 'N'){
L = u + y <= d ? rgt[u + y] : upp[v + x];
R = x + y <= d ? lft[x + y] + C : upp[x - v];
a[cnt] = Interval (L, R);
return ;
}
if (ch == 'S'){
L = y - x >= 0 ? lft[y - x] : dwn[x - y];
R = x + y < w ? dwn[x + y] : rgt[y - u];
a[cnt] = Interval (L, R);
return ;
}
if (ch == 'W'){
if (x - v > 0){
L = upp[x - v];
R = x - y > 0 ? dwn[x - y] + C : lft[y - x] + C;
}
else {
L = lft[x + y];
R = x - y > 0 ? dwn[x - y] : lft[y - x];
}
a[cnt] = Interval (L, R);
return ;
}
if (ch == 'E'){
L = x + y < w ? dwn[x + y] : rgt[y - u];
R = y + u <= d ? rgt[y + u] : upp[x + v];
a[cnt] = Interval (L, R);
return ;
}
}
inline void count ()
{
int end, cnt;
for (int i = 1; i <= n; ++ i){
if (a[i].r <= C){
end = a[i].r;
cnt = 1;
for (int j = i + 1; j <= n; ++ j){
if (a[j].r <= C && a[j].l > end){
end = a[j].r;
++ cnt;
}
else if (a[j].r > C && a[j].r - C < a[i].l && a[j].l > end){
end = a[j].r;
++ cnt;
}
}
if (end < C && a[1].r < a[i].l){
end = a[1].r;
++ cnt;
}
else if (end >= C) end -= C;
for (int j = 1; j < i; ++ j){
if (a[j].r <= C && a[j].r < a[i].l && a[j].l > end){
end = a[j].r;
++ cnt;
}
}
tot = max (tot, cnt);
}
else{
end = a[i].r - C;
cnt = 1;
for (int j = 1; j <= n; ++ j){
if (a[j].r <= C && a[j].l > end && a[j].r < a[i].l){
end = a[j].r;
++ cnt;
}
}
tot = max (tot, cnt);
}
}
return ;
}