1. 程式人生 > >noip模擬 2018 10 17

noip模擬 2018 10 17

刺客信條(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; 
}