10.16模拟赛题解

T1
Description
故事发生在1486 年的意大利,Ezio 原本只是一个文艺复兴时期的贵族,后来因为家族成员受到圣殿骑士的杀害,决心成为一名刺客。最终,凭借着他的努力和出众的天赋,成为了杰出的刺客大师。刺客组织在他的带领下,为被剥削的平民声张正义,赶跑了原本统治意大利的圣殿骑士首领-教皇亚历山大六世。在他的一生中,经历了无数次惊心动魄、扣人心弦的探险和刺杀。
这次的故事就是他暗杀一位作恶多端的红衣主教。红衣主教可以吸取他周围人的生命力量,而他的红衣教徒也拥有这个力量。红衣主教的家是一个x*y 的长方形房间,也就是说,他的家的四个角坐标分别为(0,0)(x,0)(0,y)(x,y)。教堂的门在(0,0) ,而红衣主教就在 (x,y)的卧室休息。他的家中还有n个守护着他的红衣教徒,站在(ai,bi)。Ezio想要趁主教休息时,从门进入潜入到他的卧室刺杀他,因为主教休息时会脱下红衣,这样吸取生命的力量就消失了。可是守卫他的红衣教徒依然很危险,离红衣教徒太近就会被吸取生命。因此,Ezio想知道,在能刺杀主教的前提,从门到他的卧室的路上,他最远和离他最近的红衣教徒保持多远的距离。注意:教徒都在房间里。

Sample Input
10 20 2
3 3
6 14

Sample Output

3.00

Data Constraint

数据范围
对 10%的数据n<=10,
对 30%的数据n<=100
对 100%的数据n<=2000
保证输入合法,x,y属于[1,10^6].

T2
oi_juruo热爱一款名叫黑暗之魂的游戏。在这个游戏中玩家要操纵一名有 点生命值的无火的余灰在一张地图中探险。地图中有n个篝火(也就是存档点)。在篝火处休息可以将生命值恢复满。每个篝火都会向其他篝火的其中之一连有一条通道(显然,通道是双向的),这些篝火之间都相互可达。也就是说,这是一张n个点,n条边的无向连通图。每条通道里都有一些怪物,经过oi_juruo的分析,他得到了每条边的怪物会对他造成的伤害值 .为了向oier们表演他高超的游戏技巧,他要从任意一个篝火跑到任意另一个篝火而不在之间的篝火休息,在此期间,他会和他经过的通道中的怪物战斗并损失 的生命值。现在oi_juruo想知道,他的生命值 至少为多少,才能完成任意一条旅途。oi_juruo并不傻,他会走最安全的路。本题时限为3000ms

Sample Input

5
1 2 2
2 3 2
3 4 2
1 4 1
4 5 4

Sample Output

8

n<=1e6

T3
Description

8102年,Normalgod在GLaDOS的帮助下,研制出了传送枪。但GLaDOS想把传送枪据为己有,于是把Normalgod扔进了一间实验室。这间实验室是一棵有n个节点的树。现在Normalgod在一号节点,出口也在一号节点,但为了打开它,必须经过每一个节点按下每个节点的开关,出口才能打开。GLaDOS为了杀死Normalgod,开始在实验室里释放毒气,因此Normalgod必须尽快逃出这间实验室。
当然,Normalgod手中的传送枪是可以使用的。传送枪可以发射出两个颜色不同的传送门。Normalgod可以从其中一个传送到另一个。尽管传送枪可以在视野范围内的任何一个经过特殊处理的表面打开一扇传送门,但这间实验室的设计使得Normalgod只能在他所处的房间内打开一个传送门。 在已经存在了一个同颜色的传送门时,打开新的传送门会使与它同颜色的旧门消失。传送和打开传送门所需时间为0。
显然,利用传送枪会让Normalgod更快解决谜题,可Normalgod死在了按下最后一个按钮的路上。尽管如此,GLaDOS还是很想知道到底Normalgod最快能用多久逃出去,这对她的实验室设计方法论有重要的指导作用。作为GLaDOS的算法模块,你要完成这个任务。

Input

第一行一个整数n。之后n-1行,每行三个整数ui,vi,ai ,表示有一条从ui 连向vi ,花费时间为ai 的通道。

Output

一行一个数T,表示最小的脱逃时间。

Sample Input

5
1 2 2
2 3 3
2 4 5
1 5 1

Sample Output

13

样例说明
1–> open1–> 5–> open2–> use(1)–> 2–> 3–> open2–> use(1)–> 2–> 4–> open2–> use(1)–> exit

Solution

T1
我们把左边的墙和下边的墙看做一个点,另外两堵墙看做一个点
考虑二分
每次二分到一个mid,如果两个点的距离<2*mid,那么我们就连边
如果是墙和另外的点,就是<mid的时候连边
那么我们再用并查集判断一下联通性即可

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(i=a;i<=b;++i)
using namespace std;
const int N=2e4+5;
int n,i,xx,yy,l,mid,r,fa[N],mx;
typedef long long ll;
typedef double db;
struct node{
	int x,y;
} a[N];
int read(){
	int sum=0;
	char c=getchar();
	while (c<'0'||c>'9') c=getchar();
	while (c>='0'&&c<='9') {
		sum=sum*10+c-'0';
		c=getchar();
	}
	return sum;
}
int find(int x){
	return (x==fa[x])?x:fa[x]=find(fa[x]);
}
ll get(ll x,ll y){
	return (ll)x*x+y*y;
}
bool check(ll mid){
	mid*=mid;
	int i,j,x,y;
	fo(i,1,n+2) fa[i]=i;
	fo(i,1,n-1)
	  fo(j,i+1,n) 
	    if (get(a[i].x-a[j].x,a[i].y-a[j].y)<4*mid) {
	    	x=find(i),y=find(j);
	    	if (x!=y) fa[y]=x;
		}
	mid=(ll)sqrt(mid);
	fo(i,1,n) {
		if (xx-a[i].x<mid||a[i].y<mid) {
			x=find(i),y=find(n+1);
			if (x!=y) fa[y]=x;
		} 
		if (a[i].x<mid||yy-a[i].y<mid) {
			x=find(i),y=find(n+2);
			if (x!=y) fa[y]=x;
		}
	} 
	return find(n+1)!=find(n+2);
}
int main(){
	freopen("AC.in","r",stdin);
	freopen("AC.out","w",stdout);
	xx=read(),yy=read(),n=read();
	xx*=1000,yy*=1000;
	fo(i,1,n) {
		a[i].x=read(),a[i].y=read();
		a[i].x*=1000,a[i].y*=1000;
		mx=max(a[i].x,max(mx,a[i].y));
	}
	l=0,r=mx;
	while (l<=r){
		mid=(l+r)>>1;
		if (check(mid)) l=mid+1; else r=mid-1;
	}
	printf("%.2lf\n",(db)1.0*(l-1)/1000);
}

T2
就是一个类似于基环树的东西求最长链
那么我们先把环找出来
对于环上每个互相独立的块
我们先求出其内部的直径,以及一条自环上的点开始的最长链
那么,把内部直径计算完以后
就是求最长链之和的max值
f[i]=max(f[j]-len[j]+len[i])
拆环为链的小套路
我们再用单调队列维护
代码写的比较丑就不放了

T3
首先要发现一个结论
我们不可能走到一个点传送到他的祖先再走到他的兄弟
因此我们可以考虑dp
f[i][0/1]表示从i出发,走完i的子树,再走回来,是否使用过传送门的最大值
那么显然我们用传送门会减去一条最长链的代价
完整转移见代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(i=a;i<=b;++i)
using namespace std;
const int N=1e6+5;
int n,i,x,y,z,nx[N<<1],he[N],we[N<<1],le[N<<1],tot;
long long g[N],f[N][2];
int read(){
	int sum=0;
	char c=getchar();
	while (c<'0'||c>'9') c=getchar();
	while (c>='0'&&c<='9') {
		sum=sum*10+c-'0';
		c=getchar();
	}
	return sum;
}
inline void add(int x,int y,int z){
	nx[++tot]=he[x],he[x]=tot,we[tot]=y,le[tot]=z;
}
void dfs(int x,int fr){
	int i;
	for(i=he[x];i;i=nx[i])
	  if (we[i]!=fr){
	  	  dfs(we[i],x);
	  	  g[x]=max(g[x],g[we[i]]+le[i]);
	  	  f[x][0]+=f[we[i]][0]+le[i]*2;
	  	  f[x][1]+=min(f[we[i]][0]-g[we[i]]+le[i],f[we[i]][1]+le[i]*2);
	  }
}
int main(){
	freopen("portal.in","r",stdin);
	freopen("portal.out","w",stdout);
	n=read();
	fo(i,2,n){
		x=read(),y=read(),z=read();
		add(x,y,z),add(y,x,z);	
	}
	dfs(1,0);
	printf("%lld\n",f[1][1]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值