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)。
保证
输出
对于每一个询问输出两个整数 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;
}