刺客信条(AC)
Description
故事发生在1486 年的意大利,Ezio 原本只是一个文艺复兴时期的贵族,后来因为家族成员受到圣殿骑士的杀害,决心成为一名刺客。最终,凭借着他的努力和出众的天赋,成为了杰出的刺客大师。刺客组织在他的带领下,为被剥削的平民声张正义,赶跑了原本统治意大利的圣殿骑士首领-教皇亚历山大六世。在他的一生中,经历了无数次惊心动魄、扣人心弦的探险和刺杀。
这次的故事就是他暗杀一位作恶多端的红衣主教。红衣主教可以吸取他周围人的生命力量,而他的红衣教徒也拥有这个力量。红衣主教的家是一个x*y 的长方形房间,也就是说,他的家的四个角坐标分别为(0,0)(x,0)(0,y)(x,y)。教堂的门在(0,0) ,而红衣主教就在 (x,y)的卧室休息。他的家中还有n个守护着他的红衣教徒,站在(ai,bi)。Ezio想要趁主教休息时,从门进入潜入到他的卧室刺杀他,因为主教休息时会脱下红衣,这样吸取生命的力量就消失了。可是守卫他的红衣教徒依然很危险,离红衣教徒太近就会被吸取生命。因此,Ezio想知道,在能刺杀主教的前提,从门到他的卧室的路上,他最远和离他最近的红衣教徒保持多远的距离。注意:教徒都在房间里。
Input
第一行三个整数x,y,n。之后n行,每行两个整数ai,bi ,意义见题目描述。
Output
一行一个数D,表示Ezio能保持的最大距离,保留两位小数。
Sample Input Sample Output
10 20 2 3.00
3 3 6 14
Data Constraint
对 10%的数据n<=10,
对 30%的数据n<=100
对 100%的数据n<=2000
保证输入合法,x,y属于[1,10^6].
此题本蒟蒻写法类似于noip2017 day2 t1 奶酪
我们可以用类似于奶酪这道题的做法
把每个人看做一个圆的圆心,二分枚举这些圆的半径,然后看这些圆能不能将长方形“截断”。
“截断”指四种情况:
1. 从下边界能走到左边界
2. 从下边界能走到上边界
3. 从下边界能走到右边界
4. 从上边界能走到右边界
于是可以写俩个dfs,分别从上边界搜看能不能搜到左边界和下边界,从右边界看能不能搜到左边界和下边界:
若能截断就将r = mid,否则把l = mid,这样就判断出来了。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN = 2e3 + 5;
int n,x,y;
bool vis[MAXN];
double maxdis = 0,Dis[MAXN][MAXN];
struct enemy
{
int x,y;
}a[MAXN];
void in(int &x)
{
int num = 0,f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch - '0');ch = getchar();}
x = num*f;
}
void out(int x)
{
if(x < 0) putchar('-'),x = -x;
if(x > 9) out(x/10);
putchar(x%10 + '0');
}
double f(ll x,ll y)
{
return x*x + y*y;
}
double dis(enemy one,enemy two)
{
return f(one.x - two.x,one.y - two.y);
}
bool check1(int now,double len)
{
if((a[now].y <= len) || (x - a[now].x <= len)) return 1;
vis[now] = 1;
for(int i = 1;i <= n;i++) if(Dis[now][i] <= 4*len*len && !vis[i])
if(check1(i,len)) return 1;
return 0;
}
bool check2(int now,double len)
{
if((a[now].y <= len) || (x - a[now].x <= len)) return 1;
vis[now] = 1;
for(int i = 1;i <= n;i++) if(Dis[now][i] <= 4*len*len && !vis[i])
if(check2(i,len)) return 1;
return 0;
}
bool check(double len)
{
memset(vis,0,sizeof vis);
for(int i = 1;i <= n;i++)
if(a[i].x <= len && !vis[i])
if(check1(i,len)) return 0;
memset(vis,0,sizeof vis);
for(int i = 1;i <= n;i++)
if(y - a[i].y <= len && !vis[i])
if(check2(i,len)) return 0;
return 1;
}
int main()
{
in(x); in(y); in(n);
for(int i = 1;i <= n;i++) in(a[i].x),in(a[i].y);
for(int i = 1;i <= n;i++)
for(int j = i+1;j <= n;j++)
Dis[i][j] = Dis[j][i] = dis(a[i],a[j]);
double l = 0.00,r = 1e6 + 1;
while(l + 0.001 < r)
{
double mid = (l+r)/2;
if(check(mid)) l = mid;
else r = mid;
}
printf("%.2lf",l);
return 0;
}
/*
10 10 2
9 0
10 1
*/
传送门
题目大意:
8102年,Normalgod在GLaDOS的帮助下,研制出了传送枪。但GLaDOS想把传送枪据为己有,于是把Normalgod扔进了一间实验室。这间实验室是一棵有n个节点的树。现在Normalgod在一号节点,出口也在一号节点,但为了打开它,必须经过每一个节点按下每个节点的开关,出口才能打开。GLaDOS为了杀死Normalgod,开始在实验室里释放毒气,因此Normalgod必须尽快逃出这间实验室。
当然,Normalgod手中的传送枪是可以使用的。传送枪可以发射出两个颜色不同的传送门。Normalgod可以从其中一个传送到另一个。尽管传送枪可以在视野范围内的任何一个经过特殊处理的表面打开一扇传送门,但这间实验室的设计使得Normalgod只能在他所处的房间内打开一个传送门。 在已经存在了一个同颜色的传送门时,打开新的传送门会使与它同颜色的旧门消失。传送和打开传送门所需时间为0。
显然,利用传送枪会让Normalgod更快解决谜题,可Normalgod死在了按下最后一个按钮的路上。尽管如此,GLaDOS还是很想知道到底Normalgod最快能用多久逃出去,这对她的实验室设计方法论有重要的指导作用。作为GLaDOS的算法模块,你要完成这个任务。本题时限为2000ms!
这道题是一个树形DP。
首先我们知道按所有按钮不借助传送门的话,至少需要走所有路两边。
那么用传送枪的话我们就用树形DP:
f[i][1]就代表i的祖先中有放一个传送门时的最大可以少走的路程。
f[i][0]就代表i的祖先中没放传送门时的最大可以少走的路程。
如果上面有传送门(即f[i][1]时),那么我们当前根中的子树到根的那段距离肯定是不用走的,但只能从一个子树回到根,故应取最大的那一条路径。
如果上面没有(即f[i][0]),那么说明咱这个根下子树可以用两个传送门,那么有两种情况:
一种是不在根放一个传送门,这种情况相当于可以少走所有可以用一整套(就是可以用俩个传送门)的子树方案,
另一种是指在根放一个传送门,这种情况就相当于可以少走子树到根的那一条路径和用了一个传送门走子树的方案。
具体可以看代码,很简明:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN = 1e6 + 5;
const int INF = 999999999;
int n;
ll f[MAXN][2];
struct edge
{
int next,to,v;
}e[MAXN<<1];
int head[MAXN<<1],cnt = 0;
ll ans = 0;
void add(int x,int y,int v)
{
e[++cnt].next = head[x]; e[cnt].to = y; e[cnt].v = v; head[x] = cnt;
e[++cnt].next = head[y]; e[cnt].to = x; e[cnt].v = v; head[y] = cnt;
}
void in(int &x)
{
int num = 0,f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch - '0');ch = getchar();}
x = num*f;
}
void out(ll x)
{
if(x < 0) putchar('-'),x = -x;
if(x > 9) out(x/10);
putchar(x%10 + '0');
}
void dp(int now,int fa)
{
for(int i = head[now];i;i = e[i].next)
{
int to = e[i].to,w = e[i].v;
if(to != fa)
{
dp(to,now);
f[now][1] = max(f[now][1],f[to][1] + w);
f[now][0] += max(f[to][0],f[to][1] + w);
}
}
}
int main()
{
in(n);
for(int i = 1;i < n;i++)
{
int x,y,z;
in(x); in(y); in(z);
add(x,y,z);
ans += 2*z;
}
dp(1,0);
out(ans - f[1][0]);
return 0;
}