EOJ Monthly 2019.2 (based on February Selection) A,B,E题

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个交点,以x轴为例,把它分成0~2e9和-2e9到0,分别二分找到球面上的两个点(x1,0,0),(x2,0,0),则球心的x坐标就是(x1+x2)/2,y,z也是这样处理,就求出来了。注意的是,球心范围加半径范围为 :

-2e9~2e9,这就是二分的范围,即:0~2e9,-2e9~0(这是2e9和-2e9的由来),注意:要用long long~~(二分过程中(l+r)=3e9就爆int了,这是完全可能存在的,所以用long long)

#include <iostream>
using namespace std;
typedef long long ll;
int op,f;
ll erfen(ll l,ll r){//二分
	ll ans=0;
	if(l==0){
		while(l<=r){
			ll mid=(l+r)>>1;
            //分出x,y,z坐标
			if(f==0) cout << 0 << " " << mid << " 0 0" << endl;
			else if(f==2) cout << 0 << " " << 0 << " " << mid << " 0" << endl;
			else cout << 0 << " " << "0 0 " << mid << endl;
			cin >> op;
			if(op==0) r=mid-1;
			else{
				l=mid+1;
				ans=mid;
			}
		}
	}
	else{
		while(l<=r){
			ll mid=(l+r)>>1;
            //分出x,y,z坐标
			if(f==1) cout << 0 << " " << mid << " 0 0" << endl;
			else if(f==3) cout << 0 << " " << 0 << " " << mid << " 0" << endl;
			else cout << 0 << " " << "0 0 " << mid << endl;
			cin >> op;
			if(op==0) l=mid+1;
			else{
				r=mid-1;
				ans=mid;
			}
		}
	}
	return ans;
}
int main(){
	ll x1=erfen(0,2e9);
	f++;
	ll x2=erfen(-2e9,0);
	f++;
	ll y1=erfen(0,2e9);
	f++;
	ll y2=erfen(-2e9,0);
	f++;
	ll z1=erfen(0,2e9);
	ll z2=erfen(-2e9,0);
	cout << 1 << " " << (x1+x2)/2 << " " << (y1+y2)/2 << " " << (z1+z2)/2 << endl;
	return 0;
} 

 

B. 解题 (思维+取模 )

单测试点时限: 2.0 秒

内存限制: 1024 MB

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

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

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

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

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

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

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

输入

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

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

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

保证 \sum _{i=1}^{q} mi <=5*10^7

输出

对于每一个询问输出两个整数 l,r 表示保留第 l 到第 r 位。保证一定有解。

样例

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。

题目粘贴过来有点变化,详细看原EOJ网站

题意:看提示就能懂~~

题解:就拿样例来说,1000=1249-249, 40=49-9,49=49-0 ......也就是说一个数都可以写成两个数的差,那么l令w=a-b,则w%m=0即:(a-b)%m=0,那么就是a%m=b%m.所以只需要找两个数模m相等就好了,注意b可以等于0,同时,因为抽屉原理,我们最多只要处理 m+1 个 a 就能找到答案,那么就可以写代码了,注意数据范围: 上代码:

#include <iostream>
using namespace std;
const int MAX = 1e7*5+100;//注意范围,别开小了,用题目中m的范围确定
int mp[MAX];
int main(){
	string s;
	cin >> s;
	int len=s.size();
	int p;
	cin >> p;
	while(p--){
		bool f=0;
		int x;
		scanf("%d",&x);
		int w=1;
		int ans=0;
		mp[0]=len;
		for (int i = len-1; i >= 0;i--){//倒着保证最小
			ans+=w*(s[i]-'0');//倒着求数
			w=(w*10)%x;//这里注意要模x,否则RT
			ans%=x;//推的式子
			if(mp[ans]){//标记是否模相等
				cout << i+1 << " " << mp[ans] << endl;//这里就是输出位置,因为从0开始,所以这样输出刚刚好
				f=1;
				break; 
			}
			mp[ans]=i;
		}
		if(f==0) cout << -1 << endl;
		for (int i = 0; i <= x+1;i++) mp[i]=0;//不能全部初始化,只把用到的初始化,否则T
	} 
	return 0;
} 

 

E. 中位数 (思维+spfa+二分)

单测试点时限: 10.0 秒

内存限制: 256 MB

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

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

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

一条路径的中位数指的是:一条路径有 n 个点,将这 n 个点的权值从小到大排序后,排在位置 ⌊n/2⌋+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,跑一个最长路(在跑最长路的过程中变权值),很容易知道,最长路>=0,为这个答案符合题意,二分值变大,否则不符合,二分值变小~~

注意:暴力深搜,暴力广搜,都会超时~~,只能用最长路算法跑~~

上代码:

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int inf=0x3f3f3f3f;
const int MAX = 1e6+1;
int head[MAX],dis[MAX],val[MAX];
int tot,n,m,mid;
bool vis[MAX];
struct hh{
	int u,v;
	int nt;
}a[MAX];
void add(int u,int v){
	a[tot].v=v;
	a[tot].u=u;
	a[tot].nt=head[u];
	head[u]=tot++;
}
void spfa(){//求最长路
	queue<int> q;
	q.push(1);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		vis[u]=false;
		for (int i = head[u];~i;i=a[i].nt){
			int v=a[i].v;
			if(dis[v]<dis[u]+(val[v]<mid? -1:1)){
				dis[v]=dis[u]+(val[v]<mid? -1:1);
				if(!vis[v]) {
					vis[v]=true;
					q.push(v);
				}
			}
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	memset(head,-1,sizeof(head));
	int maxx=-2e9;
	int minn=2e9;
	for (int i = 1; i <= n;i++){
		scanf("%d",&val[i]);
		maxx=max(maxx,val[i]);
		minn=min(minn,val[i]);
	}
	for (int i = 0; i < m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);//单向边,前向星不需要去重边
	}
	int l,r,ans;
	l=minn;
	r=maxx;
	ans=-inf;
	while(l<=r){//二分答案
		mid=(l+r)>>1;
		memset(dis,-inf,sizeof(dis));
		memset(vis,false,sizeof(vis));//这个不写也可以,很迷,我觉得必须写
		if(val[1]>=mid) dis[1]=1;
		else dis[1]=-1;
		spfa();
		if(dis[n]>=0){
			ans=mid;
			l=mid+1;
		}
		else r=mid-1;
	}
	if(ans==-inf) puts("-1");//别忘了走不到输出-1
	else printf("%d\n",ans);
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心脏dance

如果解决了您的疑惑,谢谢打赏呦

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值