【NOIP2019模拟赛19.8.20】

#题目
A【NOIP2018模拟赛19.8.20】妹子
B【NOIP2018模拟赛19.8.20】老大
C【NOIP2018模拟赛19.8.20】旅程

A. 【NOIP2018模拟赛19.8.20】妹子

这题坑点很多,,,第一遍打代码完全没动脑子,直接比较长宽,做完之后总感觉不对劲 ,想了想发现可以翻转放置,,,结果,,居然还可以斜着放!!

打了一个不像正解的代码,枚举矩形旋转的角度,然后用三角函数的知识求出小矩形的边在大矩形上的映射,在比较长度就可以了

#include<bits/stdc++.h>
#define pai 3.1415926 
using namespace std;
int main(){
//	freopen("girls.in","r",stdin);
//	freopen("girls.out","w",stdout);
	int t;
	scanf("%d",&t);
	while(t--){
		int a1,b1,a2,b2;
		scanf("%d%d%d%d",&a1,&b1,&a2,&b2);
		if((a1<=a2&&b1<=b2)||(b2<=b1&&a2<=a1)||(a1<=b2&&b1<=a2)||(a2<=b1&&b2<=a1))printf("Yes");
		else{
			if(a1*b1>a2*b2)swap(a1,a2),swap(b1,b2);
		int flag=0;
		for(double i=0;i<=90;i+=0.01){
			if(b1*sin(i*pai/180)+a1*cos(i*pai/180)<=a2&&b1*cos(i*pai/180)+a1*sin(i*pai/180)<=b2){printf("Yes");flag=1;break;
			}
		}
		if(!flag)printf("No");
		}
		if(t)printf("\n");
	}
	return 0;
}

B. 【NOIP2018模拟赛19.8.20】老大

考试时想的是先找整棵树的重心,然后在重心的每棵子树分别找重心,,但时间不够没打完,其实这个做法的正确性我也完全没证明,不过还是拿了30分

正解有个结论是:奖杯一定会放在整棵树的直径上,用反证法证明一下就好了。

而奖杯要覆盖所有点前提就是一定要覆盖直径的两个端点,于是二分枚举端点到奖杯的距离,check函数就检查两个奖杯之间的子树是否符合要求就可以了,因为端点到奖杯之间的子树一定是符合条件的,依然可以用反证法,如果这之间用一个子节点到奖杯的距离大于端点到奖杯的距离,那这个节点到另一个直径端点的距离就大于现在的直径了,显然不可能

#include<bits/stdc++.h> 
#define inf 99999999
using namespace std;
int n,s,t;
struct node{
	int e,n;
}pr[1001000];
int f[1001000],in[1001000],head[1001000],idx,dep[1001000],son[1001000];
void add(int u,int v){
	pr[++idx].e=v;
	pr[idx].n=head[u];
	head[u]=idx;
}
int read(){
	char ch;
	int flag=1;
	ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')flag=-1;
		ch=getchar();
	}
	int sum=0;
	while(ch>='0'&&ch<='9'){
		sum=sum*10+ch-'0';
		ch=getchar();
	}
	return flag*sum;
}
void dfs1(int x,int fa){
	f[x]=fa;
	for(int i=head[x];i;i=pr[i].n){
		int y=pr[i].e;
		if(y==fa)continue;
		dep[y]=dep[x]+1;
		dfs1(y,x);
	}
}
int dis[1001000];
void dfs2(int x){
	dis[x]=dep[x];
	for(int i=head[x];i;i=pr[i].n){
		int y=pr[i].e;
		if(y==f[x]||in[y])continue;
		dfs2(y);
		dis[x]=max(dis[x],dis[y]);
	}
}
bool check(int mid){
	int a=s,b=t;
	while(a!=t){
		if(dep[a]-dep[s]==mid)break;
		a=son[a];
	}
	while(b!=s){
		if(dep[t]-dep[b]==mid)break;
		b=f[b];
	}
	if(dep[b]-dep[a]>2*mid)return 0;
	int ll=a,rr=b;
	while(a!=b){
		dfs2(b);
		int d=dis[b]-dep[b];
		if(min(dep[b]-dep[ll],dep[rr]-dep[b])+d>mid)return 0;
		b=f[b];
	}
	dfs2(b);
	int d=dis[b]-dep[b];
	if(min(dep[b]-dep[ll],dep[rr]-dep[b])+d>mid)return 0;
	return 1;
}
int main(){
//	freopen("ob.in","r",stdin);
//	freopen("ob.out","w",stdout);
	n=read();
	for(int i=1;i<n;i++){
		int x,y;
		x=read();
		y=read();
		add(x,y);
		add(y,x);
	}
	dfs1(1,0);
	for(int i=1;i<=n;i++)if(dep[i]>dep[s])s=i;
	memset(dep,0,sizeof(dep));
	dfs1(s,0);
	for(int i=1;i<=n;i++)if(dep[i]>dep[t])t=i;
	
	int now=t;
	while(now){
		in[now]=1;
		son[f[now]]=now;
		now=f[now];
	}
	in[s]=in[t]=1;
	int l=0,r=dep[t],ans;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid))r=mid-1,ans=mid;
		else l=mid+1;
	}
	printf("%d",ans);
	return 0;
}

C. 【NOIP2018模拟赛19.8.20】旅程

 考场上我直接每次硬核删边然后跑spfa,只有40

正解思路很巧妙,转换解题思路,把删边操作转换为加边,这样我们就可以每次用floyed维护,有个细节是因为可以重复删边,所以我们每次要记录第一次删边操作是哪一步,只有读取到记录的这一步才能加边

#include<bits/stdc++.h> 
#define inf 1e15
#define ll long long
using namespace std;
int n,m,top,v[1001][1001];
ll d[1001][1001],ans[100100],a[1001][1001];
struct node{
	int c,x,y;
}pr[1001000];
int main(){
//	freopen("journey.in","r",stdin);
//	freopen("journey.out","w",stdout);
	memset(a,127,sizeof(a));
	memset(v,127,sizeof(v));
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++)scanf("%lld",&a[i][j]),d[i][j]=a[i][j];
	}
	for(int i=1;i<=n;i++)a[i][i]=d[i][i]=0;
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&pr[i].c,&pr[i].x,&pr[i].y);
		if(pr[i].c==1){
			a[pr[i].x][pr[i].y]=inf;
			v[pr[i].x][pr[i].y]=min(v[pr[i].x][pr[i].y],i);
		}
	}
	for(int k=1;k<=n;k++)
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	if(i!=j&&i!=k&&k!=j)a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
	
	for(int i=m;i>=1;i--){
		if(pr[i].c==2)ans[++top]=a[pr[i].x][pr[i].y];
		else{
			int x=pr[i].x,y=pr[i].y;
			if(v[x][y]!=i)continue;
			a[x][y]=min(a[x][y],d[x][y]);
			for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)a[i][j]=min(a[i][j],a[i][x]+a[x][y]+a[y][j]);
		}
	}
	for(int i=top;i>=1;i--){
		printf("%lld",ans[i]);
		if(i>1)printf("\n");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值