备战Noip2018模拟赛15(A组)T3 Clock 淘淘起床了

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。

保证人的坐标不在边界上,但不保证坐标不重复。


思路

模拟+贪心

这道题真的超级考恶心的细节啊

先把问题简单化,将每个分身的视野范围对应到区间上,然后这个问题就变成了,在一个环上有许多区间,求最大不相交区间数,说起来很简单,但是对于如何将视野对应到区间上还是要仔细一些,具体方法看后面;

在线段上求最大不相交区间数是很常见的,如果到了环上,可以把每个区间作为开始的第一个区间枚举,再使用贪心求解,最后取最大值,复杂度\ theta \ left(n ^ 2 \ right)

再来说一些细节部分

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为例,若x + y \ leqslant d,说明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 ;
}

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值