Contest - zjnu province training(9)

这篇博客介绍了ACM竞赛中的模拟题型,包括键盘错位问题的解决方案,以及两种不同的数学问题:序列计算和线段计数。同时,还涉及到一个逻辑推理题目,关于判断机器人是否撒谎。最后,文章讨论了一个几何难题,涉及动态圆的碰撞问题,给出了问题的几何解析和解题思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

键盘

Case Time Limit:1000MS
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 28 Accepted: 17

Description

大家都知道,现在使用键盘的是下列字母:

qwertyuiop
asdfghjkl;
zxcvbnm,./

BSNY在联系盲打,但是有时候,他盲打会偏移一个,或者左偏或者右偏,如果左偏,比如BSNY想打f,实际按了d;反之,如果右偏,想打d,实际打了f。

为了方便,我们用L,R表示左偏和右偏。

给你BSNY打出来的字母,问BSNY原本想打的字母是什么?

Input

先输入L或R

然后一行字符

Output

输出BSNY原本想打的字符

Sample Input

 
 
R s;;upimrrfod;pbr

Sample Output

 
 
allyouneedislove

Hint

保证打的字符在上述键盘框内, 字符串长度小于10000

这是一道水题,就是模拟,但是读题要仔细啊,它给出的字符串是现在的,然后L,R代表的是他经过了什么变化而输出现在的字串的,然后叫你输出原来的字符串。

我的方法是分情况讨论,L,R,然后每次for一遍所有的键盘,然后再输出。

但是有更好的方法:(直接上代码)

#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
int main(){
	string s = "qwertyuiopasdfghjkl;zxcvbnm,./";
	char d;
	cin >> d;
	string p;
	cin >> p;
	for (int i = 0; i < p.size(); i++) {
		int ch = s.find(p[i]);
		if (d=='L') cout << s[ch+1];
		else cout << s[ch-1];
	}
	cout<<endl;
	return 0;
}
它的思想就是把所有的都存进去,然后再在里面判断是L还是R,然后输出+1操作还是-1操作。


函数计算

Case Time Limit:1000MS
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 27 Accepted: 14

Description

对于一个正整数n,我们可以定义一个函数:

f(n) =  -1 + 2 - 3 + .. + ( - 1)nn

 

现在给你一个n,希望你可以算出f(n)

这也是一道大水题,但是不能直接按照题目说的来死算,是要找规律的。

#include<stdio.h>
#include<string.h>
typedef __int64 ll;
ll f;
int main(){
	ll n;
	int i,j,k;
	scanf("%I64d",&n);
	f=0;
	if(n%2) f=(n/2+1)*(-1);
	else f=n/2;
	printf("%I64d\n",f);
}

线段

Case Time Limit:1000MS
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 91 Accepted: 18

Description

有N条线段,已知每条线段的起点和终点(1~500000内的整数),然后有M个询问,每次询问一个点(1~500000内的整数),求这个点在多少条线段上出现过?

Input

第一行N线段条数
接下来N行,每行两个数,线段的起点和终点
第N+2行一个数M询问个数
接下来M行,每行一个点

Output

对于每个询问,求答案

这道题也是简单题,但是里面有一点就是不能直接for一遍求答案,会爆掉,且这里要用到一个前缀和的东西。

#include<stdio.h>
#include<string.h>
__int64 a[555555];
int main(){
	int n,i,j,k,m;
	scanf("%d",&n);
	memset(a,0,sizeof(a));
	int s,t;
	for(i=1;i<=n;i++){
		scanf("%d%d",&s,&t);
		a[s]++;
		a[t+1]--;
	}
	for(i=1;i<=500001;i++) a[i]+=a[i-1];
	scanf("%d",&m);
	__int64 sum=0;
	while(m--){
		sum=0;
		scanf("%d",&k);
		sum=a[k];
		printf("%I64d\n",sum);
	}
}

谁在说谎

Case Time Limit:1000MS
Time Limit: 6000MS Memory Limit: 65536K
Total Submissions: 34 Accepted: 11

Description

有n个小朋友,编号1到n,老师将他们分成两队,但分完后小朋友又乱跑了,不知道谁分在哪队,于是他求助很多小机器人,编号1到m,这些机器人会按顺序提示一些信息。

提示的信息是这样的: a b, 表示小朋友a和小朋友b不在同一队。

但是这些机器人偶尔会撒谎,老师就来请求你帮忙,看看哪个机器人最先撒谎。撒谎的规则是这样的,只要当前机器人说的信息与之前所有信息不冲突,就不算撒谎,如果冲突了,就算撒谎了。

Input

输入n,m

然后输入m行,第i行为机器人i提供的信息

Output

如果没机器人撒谎,输出”OK”

否则输出第一个撒谎机器人的编号

Sample Input

  
  
3 3 1 2 2 3 1 3

Sample Output

  
  
3

Hint

前两个机器人说的没冲突,得出1和3应该在同一队,所以第三个机器人撒谎了

1<=n<=5000  1<=m<=100000

这道题是用并查集做的,而且是普通的那种,好奇妙啊,我不是很会用,但是多看就好了。

#include<stdio.h>
#include<string.h>
int f[5555*2],n,m;
#define maxn 111110
int x[maxn],y[maxn];
#define MAXN 2*n
void init(){
	for(int i=1;i<=MAXN;i++) 
		f[i]=i;
}
int getf(int v){
	if(f[v]==v)
		return v;
	else {
		f[v]=getf(f[v]);
		return f[v];
	}
}
int main(){
	int i,j,k;
	scanf("%d%d",&n,&m);
	init();
	for(i=1;i<=m;i++) scanf("%d%d",&x[i],&y[i]);
	int flag=1;
	for(i=1;i<=m;i++){
		int xx=getf(x[i]);
		int yy=getf(y[i]);
		//如果两个人的祖先相同,那么肯定是不成立的; 
		if(xx==yy){
			flag=0;
			printf("%d\n",i); 
			break;
		}
		else{
			f[xx]=getf(y[i]+n);
			f[yy]=getf(x[i]+n);
		}
	}
	if(flag) printf("OK\n");
}
其原理就是类似于不停的把它们合并;

注意这里xx合并到y[i]+n,因为它肯定是和它们不在同一个集合内的。

而yy合并到x[i]+n.同理。


射击

Case Time Limit:1000MS
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 29 Accepted: 11

Description

BSNY在练打枪,他固定站在一个位置,不远处有个点会朝着某个方向释放飞盘,现在BSNY想知道,他能否击中这个飞盘。

为了使得问题简单,我们可以将射击场看成一个平面。BSNY所站的位置视为点(x1, y1), 发射飞盘的位置也视为点(x0, y0)。飞盘飞行的速度和方向我们用速度向量表示(dx, dy)。飞盘视为圆,刚开始圆心落在发射点,半径为r0, 子弹也视为圆,刚开始圆心落在BSNY站的点,半径为r1, 子弹的速度为v,方向可以任意方向发射。

问,BSNY发射的子弹能否打到飞盘(边缘接触到就算打中)。

能打中,输出最少花费的时间(保留4位小数); 不能打中,输出”Impossible”

Input

输入T,表示有T组测试数据

对于每组测试数据,第一行输入x0, y0

第二行输入x1, y1

第三行输入dx, dy

第四行输入r0, r1

第五行输入v

Output

能打中,输出最少花费的时间(保留4位小数); 不能打中,输出”Impossible”

Sample Input

  
  
2 0.0 0.0 4.0 0.0 1.0 0.0 1.0 1.0 2.0 0.0 0.0 300.0 400.0 -3.0 -4.0 1.0 1.0 5.0

Sample Output

  
  
0.6667 Impossible

Hint

T<=100  其余参数可能为小数,绝对值不超过10000

这道题主要考察的就是几何知识,只要把方程列出来就好了。

这里附上题解:

比较难的几何题。几何模型是两个圆,一个跑一个追,我们称跑的那个圆为跑圆,追的那个圆为追圆。跑圆的速度大小和方向是确定的,但追圆的速度大小定了,方向没定。我们很容易想到的就是把跑圆的半径反加到追圆上面,设反加后追圆的半径为R,这样就变成了追圆和跑点的问题。但是后面怎么办就不大好想了。考虑追圆的圆心,如果它的速度大小是v, t时间后,它的圆心轨迹实际上是半径为vt的圆(圆心可覆盖的所有位置就是这个圆的全部)。那么t时间追圆能覆盖的全部位置实际上是(vt+R)的圆的全部。(全部是指包含边界和内部),那么实际上可以看作追圆圆心不变,它的半径不断加大,问何时能碰上跑点。于是问题就简单了,t时刻后跑点的位置是可以表示的,列出它到追圆圆心距离的平方的式子,让其<=R*R,化简后这是是一个2次不等式。at^2+bt+c<=0,后面就是分类讨论了,注意a==0时,退化成一次。否则的话就是抛物线了,用2次方程根与系数的关系等,可以算出结果。数据比较厚道,eps设置为1e-10,1e-9,1e-8,1e-7,1e-6应该都能过的。
腾讯那道就更简单了,直接列方程,将未知数将到只有T,然后解方程就行
其实就是把飞盘的半径附到子弹上去,然后把子弹当成一个园,求它能覆盖到的最大面积就好了。

#include<stdio.h>
#include<string.h>
#include<math.h>
#define eps 1e-9
#define sr(x) (x*x)
int main(){
	double x0,y0,x1,y1,dx,dy,r0,r1,v;
	int T,i,j,k;
	scanf("%d",&T);
	while(T--){
		scanf("%lf%lf",&x0,&y0);
		scanf("%lf%lf",&x1,&y1);
		scanf("%lf%lf",&dx,&dy);
		scanf("%lf%lf",&r0,&r1);
		scanf("%lf",&v);
		double a,b,c;
		a=sr(dx)+sr(dy)-sr(v);
		b=2*x0*dx+2*y0*dy-2*x1*dx-2*y1*dy-2*(r1+r0)*v;
		c=sr(x0)+sr(y0)-2*x0*x1-2*y0*y1+sr(x1)+sr(y1)-sr((r0+r1));
		//1:当系数a为0时; 
		if(fabs(a)<=eps){
			double time=0;
			time=-c/b;
			if(time<=eps) puts("Impossible");
			else printf("%.4lf\n",time);
		}
		else {
			//求出根来,若不存在,则说明是不可能的; 
			double data=(double)(b*b-4.0*a*c);
			if(data<=eps) puts("Impossible");
			else{
				//xx代表的是第一个求出来的根,yy是第二个; 
				double xx,yy;
				xx=(-b+sqrt(data*1.0))/(2.0*a);
				yy=(-b-sqrt(data*1.0))/(2.0*a);
				if((xx<=eps)&&(yy>=eps)) printf("%.4lf\n",yy);
				else if(xx>=eps&&yy>=eps){
					if(xx-yy>=eps) printf("%.4lf\n",yy);
					else printf("%.4lf\n",xx);
 				}
 				else if(xx<=eps&&yy<=eps) puts("Impossible");
			}
		}
	}
}
总之,还是不错的,继续加油吧,hades!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值