EOJ Monthly 2019.2 (based on February Selection)

本文解析了一场算法挑战赛的六道题目,包括卫星回收、数学难题、进制转换、中位数寻找、方差计算和射箭成绩分析。通过详细的题解和代码示例,展示了如何运用算法解决实际问题。

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

题目链接:点击查看

A. 回收卫星

单测试点时限: 1.0 秒

内存限制: 256 MB

“这个世上没有无用的齿轮,也只有齿轮本身能决定自己的用途。”

就像太空中的卫星,虽然不计其数,但都各司其职。

但没有一个东西是能够永远无损的。为了便于回收及修理,卫星在故障后会生成一个球形的星场,与之配对的感应器能判断其是否在星场内。

QQ 小方现在要负责卫星的回收工作,他负责使用这些感应器锁定卫星的位置。QQ 小方有卫星基本的运动信息,因此每次工作时,他都能保证自己与卫星保持相对静止,以及自己在星场内。因此,可以将 QQ 小方看作空间直角坐标系的原点 (0,0,0),而星场是一个中心坐标为 (x,y,z) (−109≤x,y,z≤109) ,半径为 r (1≤r≤109) 且包含原点的球,其中 x,y,z,r 都是整数。但接下来,QQ 小方需要你的帮助。

为了回收卫星,QQ 小方每次能向一个整点发射一个感应器,感应器会返回它是否在星场里。由于经费紧张,QQ 小方只能发射不超过 200 个感应器,你能帮助他找到卫星的具体坐标 (x,y,z) 吗?

星场边界上的点视为在星场内部。

交互流程

每一行输出四个整数 0,xi,yi,zi,代表向 (xi,yi,zi) 发射一个感应器。随后,交互程序会输出 1 / 0,代表感应器在 / 不在星场内。

收集足够多的数据之后,输出四个整数 1,x,y,z,代表确定卫星的坐标是 (x,y,z)。之后,你的程序不应再有任何输出。

样例

Input

0
0
0
0
0
0

Output

0 2 0 0
0 -2 0 0
0 0 2 0
0 0 -2 0
0 0 0 2
0 0 0 -2
1 0 0 0

提示

对于样例:

球场的中心是 (0,0,0),半径为 1。

在交互过程中,依次访问了 (2,0,0), (−2,0,0), (0,2,0), (0,−2,0), (0,0,2), (0,0,−2) 六个点,但这六个点都不在球场内。因为原点在球场内,所以球场中心一定位于 (0,0,0),半径为1。(当然,也有可能是运气好,但这个球的中心的确是(0,0,0) 。)

题解:因为原点肯定在球内,所以我们可以二分出上下两个交点,根据弦的性质可得,圆心在两点的垂直平分线上,因此6次二分,即可确定圆心,注意,因为判断的范围在(-2e9,2e9),所以需要用 long long.。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
	ll l,r;
	int op;
	ll x,y,z;
	ll ans;
	l=0,r=2e9;
	while(l<=r)
	{
		ll mid=(l+r)>>1;
		cout<<0<<" "<<mid<<" "<<0<<" "<<0<<endl;
		scanf("%d",&op);
		if(op)
		{
			ans=mid;
			l=mid+1;
		}
		else r=mid-1;
	}
	x=ans;
	l=-2e9,r=0;
	while(l<=r)
	{
		ll mid=(l+r)>>1;
		cout<<0<<" "<<mid<<" "<<0<<" "<<0<<endl;
		scanf("%d",&op);
		if(op)
		{
			ans=mid;
			r=mid-1;
		}
		else l=mid+1;
	}
	x=(ans+x)>>1;
	
	l=0,r=2e9;
	while(l<=r)
	{
		ll mid=(l+r)>>1;
		cout<<0<<" "<<0<<" "<<0<<" "<<mid<<endl;
		scanf("%d",&op);
		if(op)
		{
			ans=mid;
			l=mid+1;
		}
		else r=mid-1;
	}
	z=ans;
	l=-2e9,r=0;
	while(l<=r)
	{
		ll mid=(l+r)>>1;
		cout<<0<<" "<<0<<" "<<0<<" "<<mid<<endl;
		scanf("%d",&op);
		if(op)
		{
			ans=mid;
			r=mid-1;
		}
		else l=mid+1;
	}
	z=(ans+z)>>1;
	
	l=0,r=2e9;
	while(l<=r)
	{
		ll mid=(l+r)>>1;
		cout<<0<<" "<<0<<" "<<mid<<" "<<0<<endl;
		scanf("%d",&op);
		if(op)
		{
			ans=mid;
			l=mid+1;
		}
		else r=mid-1;
	}
	y=ans;
	l=-2e9,r=0;
	while(l<=r)
	{
		ll mid=(l+r)>>1;
		cout<<0<<" "<<0<<" "<<mid<<" "<<0<<endl;
		scanf("%d",&op);
		if(op)
		{
			ans=mid;
			r=mid-1;
		}
		else l=mid+1;
	}
	y=(ans+y)>>1;
	
	cout<<1<<" "<<x<<" "<<y<<" "<<z<<endl;
	return 0;
}

 

B. 解题

单测试点时限: 2.0 秒

内存限制: 1024 MB

“我把房门上锁,并非为了不让她进去,而是为了防止自己逃到她身边”。

她又被数学难住了。QQ 小方当然是不会对女生说”不”的。

她的数学题是这样的,她得到了一个十进制大整数,这个大整数只包含 1 - 9 这 9 个数字。

现在,要求选出其中连续的一段数字,把其他未被选中的数字全部变成 0,并且使得变换以后的大整数恰好是 m 的倍数。

QQ 小方为了表现自己的能力,所以一口答应给她写出在所有可能的数里面最小的一个。

但是她的问题太多了,她对于这一个大整数,需要对于 q 个不尽相同的 m 分别给出答案。

但是 QQ 小方自己不会。只能来求助你了,你能帮他解答吗?

输入

第一行包含一个大整数,这个整数的位数为 n (1≤n≤106)。

第二行一个整数 q (1≤q≤500) 代表询问次数。

对于每一个询问,包含一行一个整数,表示第 i 次询问的 mi (1≤mi≤5×107)。

保证 ∑qi=1mi≤5×107 。

输出

对于每一个询问输出两个整数 l,r 表示保留第 l 到第 r 位,如果不存在这样的答案,请输出 -1

样例

input

1249
4
7
3
2
83

output

3 4
4 4
3 3
2 4

提示

对于样例:
1249 这个数中,可选出的最小的7的倍数是49,最小的3的倍数是9,2的倍数是40,83的倍数是249。

题解:因为找的一个区间(l,r),所以 l 到末尾 和 r 到末尾 膜m的结果相同,利用这一结论,把每个膜m得到的结果位置标记一下即可,之前出现过直接输出对应位置即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
const int N=1e6+10;
char s[N];
int q,m,mp[N*50];
int main()
{
	scanf("%s",s+1);
	int len=strlen(s+1);
	scanf("%d",&q);
	while(q--)
	{
	//	map<int,int> mp;
		scanf("%d",&m);
		int ans=0,p=1;
		int flag=0;
		mp[0]=len+1;
		for(int i=len;i>=1;i--)
		{
			ans+=p*(s[i]-'0');
			ans%=m;
			p=p*10%m;
		//	cout<<ans<<endl;
			if(mp[ans])
			{
				printf("%d %d\n",i,mp[ans]-1);
				flag=1;
				break;
			}
			mp[ans]=i;
		}
		if(!flag) printf("-1\n");
		for(int i=0;i<=m;i++) mp[i]=0;
	}
	return 0;
} 

D. 进制转换

单测试点时限: 2.0 秒

内存限制: 256 MB

“他觉得一个人奋斗更轻松自在。跟没有干劲的人在一起厮混,只会徒增压力。”

QQ 小方决定一个人研究研究进制转换。

很快,QQ 小方就遇到问题了。他现在想知道在十进制范围 [l,r] 内有多少整数满足在 k 进制下末尾恰好有 m 个 0。

比如在十进制下的 24 在二进制下是 11000,我们称十进制下的 24 在二进制下末尾恰好有 3 个 0。

QQ 小方一筹莫展,你能帮他解决问题吗?

输入

第一行包含一个整数 T (1≤T≤105) 表示数据组数。

对于每组数据包含一行,四个整数 l,r,k,m ( 1≤l≤r≤1018, 2≤k,m≤100),含义如题目所述。

输出

对于每组数据输出一行,包含一个整数,表示答案。

样例

input

2
1 10 2 3
1 100 2 3

output

1
6

提示

例如,在 100 进制下,末位是 90 的数不算作有末尾 0。

题解:r以为的符合条件的 - l以为的符合条件的      符合条件为末尾至少为m个0的 - 至少为m+1个0的  我们把乘法运算转化为除法,就可以避免越界了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
const int N=1e6+10;
int main()
{
	int T;
	scanf("%d",&T);
	ll l,r,k,m;
	while(T--)
	{
		scanf("%lld%lld%lld%lld",&l,&r,&k,&m);
		ll n,ans1,ans2;
		l--;
		n=m;
		while(n)
		{
			n--;
			r/=k;
			l/=k;
		}
		ans1=r-r/k;
		ans2=l-l/k;
		printf("%lld\n",ans1-ans2);
		
	}
	return 0;
} 

E. 中位数

单测试点时限: 10.0 秒

内存限制: 256 MB

“你的地图是一张白纸,所以即使想决定目的地,也不知道路在哪里。”

QQ 小方最近在自学图论。他突然想出了一个有趣的问题:

一张由 n 个点,m 条边构成的有向无环图。每个点有点权 Ai。QQ 小方想知道所有起点为 1 ,终点为 n 的路径中最大的中位数是多少。

一条路径的中位数指的是:一条路径有 n 个点,将这 n 个点的权值从小到大排序后,排在位置 ⌊n2⌋+1 上的权值。

输入

第 1 行输入两个正整数 n,m (1≤n≤106,1≤m≤106),表示结点数量和边的数量。

第 2 行输入 n 个由空格隔开的整数 Ai (0≤Ai≤109),表示点权。

接下来 m 行,每行输入两个整数 x,y (1≤x,y≤n),表示有一条 x 指向 y 的单向边,保证给出的图是联通的,可能存在重边。

输出

输出一行包含一个整数,表示最大的中位数。如果不存在任何一条起点为 1 ,终点为 n 的路径,则输出 −1 。

样例

input

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

output

4

题解:二分枚举下答案,权值大于等于的令其为1,小于的为-1,跑一边最长路,若dis[n] >=0则该情况符合

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
const int N=1e6+10;
vector<int> v[N];
int val[N],b[N],a[N];
int ans;
int n,m;
struct node{
	int to,d;
	bool operator <(const node &x)const
	{
		return d<x.d;
	}
};
int dis[N],vis[N];
int judge(int mid)
{
	for(int i=0;i<=n;i++)
	{
		vis[i]=0;
		dis[i]=-INF;
	}
//	cout<<mid<<endl;
	priority_queue<node> q;
	node tmp,now;
	dis[1]=((val[1]>=mid)?1:-1);
	vis[1]=1;
	tmp.d=dis[1];
	tmp.to=1;
	q.push(tmp);
	while(!q.empty())
	{
		now=q.top();q.pop();
		vis[now.to]=0;
		for(int i=0;i<v[now.to].size();i++)
		{
			int to=v[now.to][i];
			if(dis[to]<dis[now.to]+((val[to]>=mid)?1:-1))
			{
				dis[to]=dis[now.to]+((val[to]>=mid)?1:-1);
				if(!vis[to])
				{
					vis[to]=1;
					tmp.to=to;
					tmp.d=dis[to];
					q.push(tmp);	
				}	
			} 
		}
	}
	return dis[n]>=0;
}
int main()
{
	ans=-1;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&val[i]);
	int x,y;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		v[x].push_back(y);
	}
	int l=0,r=1e9;
	while(l<=r)
	{
		int mid=(r+l)>>1;
		if(judge(mid))
		{
			l=mid+1;
			ans=mid;
		}
		else r=mid-1;
	}
	printf("%d\n",ans);
	return 0;
} 

 

F. 方差

单测试点时限: 2.0 秒

内存限制: 256 MB

“放弃不难,但坚持一定很酷。”

QQ 小方已经在体育馆苦练一天射箭了,但他还在坚持。

QQ 小方每天都要在朋友圈晒自己的训练记录。他一共进行了 n 次射箭,成绩分别是 x1,x2,⋯,xn。为了表现自己的发挥十分稳定,QQ 小方决定选出其中的 m 次成绩,使得他们的方差是所有可以选择的方案中最小的。

对于 m 个元素组成的数列 a1,a2,⋯,am,我们知道他们的方差 σ2=(a1−a¯)2+(a2−a¯)2+⋯+(am−a¯)2m ,其中 a¯=a1+a2+⋯+amm。

但是这个问题对 QQ 小方来说太难了,你需要去帮助 QQ 小方。

为了方便,现在你需要输出这个最小的 σ2 乘以 m2 以后的结果。

输入

输入一行包含两个正整数 n (1≤n≤106) 和 m (1≤m≤n)。接下来一行包含 n 个整数 x1,x2,⋯,xn (1≤xi≤103)。

输出

输出一行包含一个整数,表示答案。为了方便,我们需要输出最小的 σ2 乘以 m2 以后的结果。

样例

input

5 3
1 2 3 4 5

output

6

题解:不一个个写了,直接看题解吧 https://acm.ecnu.edu.cn/blog/entry/320/,不过我当时每台注意,傻逼了,维护了连续m-1个,然后逐渐向后维护的连续m个的

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
const int N=1e6+10;
int n,m;
ll x[N],sum[N];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%lld",&x[i]);
	if(m==0) printf("0\n");
	else
	{
		sort(x+1,x+1+n);
		for(int i=1;i<=m-1;i++) sum[1]+=x[i];
		
		for(int i=m;i<=n;i++) sum[i-m+2]=sum[i-m+1]-x[i-m+1]+x[i];
		
		ll ans=0,cnt,minn;
		
		for(int i=1;i<=m;i++) ans+=x[i]*x[i]*m;
		ans-=(sum[1]+x[m])*(sum[1]+x[m]);
		minn=ans;
	//	cout<<minn<<endl;
		for(int i=m+1;i<=n;i++)
		{
			
			cnt=ans;
			cnt-=x[i-m]*x[i-m]*m-m*x[i]*x[i];
			cnt+=(x[i-m]*x[i-m]-x[i]*x[i]+2*x[i-m]*sum[i-m+1]-2*x[i]*sum[i-m+1]);
		//	cout<<cnt<<endl;
			ans=cnt;
			
			minn=min(minn,cnt);
		}
		printf("%lld\n",minn);
	}
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值