MX-X12-T7 / ALFR R5G1 地铁(Easy Version)题解
Subtask 1
数据范围很小,可以用很多种方法通过。甚至可以手画所有
#include<bits/stdc++.h>
using namespace std;
int ans[15][15]={{0},
{0,1,1,1,1,1,1,1,1,1,1},
{0,1,2,2,2,2,2,2,2,2,2},
{0,1,2,2,3,3,3,3,3,3,3},
{0,1,2,3,3,3,4,4,4,4,4},
{0,1,2,3,3,4,4,4,4,5,5},
{0,1,2,3,4,4,4,5,5,5,5},
{0,1,2,3,4,4,5,5,5,6,6},
{0,1,2,3,4,4,5,5,6,6,6},
{0,1,2,3,4,5,5,6,6,6,7},
{0,1,2,3,4,5,5,6,6,7,7}};
int main(){
ios::sync_with_stdio(0);cin.tie(0);
int t;cin>>t;
while(t--){
int n,m;cin>>n>>m;
cout<<ans[n][m]<<"\n";
}
return 0;
}
你可能想要尝试找规律来通过这题:
斜着读并放到 OEIS 里面搜,可以搜到 A163994,但是实际上本题与 A163994 并不相同。例如
把最小的使得
Subtask 2
通过手画找规律之类的方法可以得知此时的答案为
#include<bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(0);cin.tie(0);
int t;cin>>t;
while(t--){
int n,m;cin>>n>>m;
cout<<(int)ceil(2.0/3.0*n)<<"\n";
}
return 0;
}
Subtask 3
首先,每条线路都只可能是从左上到右下,或者从左下到右上的。设这两种线路的数量分别为
假设只有一条这样的线路,那么它最多经过
只考虑一个方向的线路的其中一个角落,在这里放进
首先,到左上角的距离为
另外别忘了考虑不同向线路交叉产生的
原问题就可以转化为:找到最小的
考虑枚举所有可能的
#include<bits/stdc++.h>
using namespace std;
long long n,m;
bool check(long long x,long long y){
return x*y>=(x+y)*(x+y)-(x+y)*(n+m)+n*m;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
int t;cin>>t;
while(t--){
cin>>n>>m;
int ans=1e9;
for(int x=0;x<=min(n,m)/2+1;x++){
int l=1,r=min(n,m);
while(l<r){
int mid=(l+r)>>1;
if(check(x,mid)){
r=mid;
}else l=mid+1;
}
ans=min(ans,x+l);
}
cout<<ans<<"\n";
}
return 0;
}
Subtask 4
注意到枚举出一个
#include<bits/stdc++.h>
using namespace std;
long long n,m;
bool check(long long x,long long y){
return x*y>=(x+y)*(x+y)-(x+y)*(n+m)+n*m;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
int t;cin>>t;
while(t--){
cin>>n>>m;
long long ans=min(n,m);
for(long long x=0;x<=min(n,m)/2+1;x++){
long long b=-(n+m-x),c=n*m-(n+m-x)*x;
long long y=ceil((-b-sqrt(b*b-4*c))/2);
ans=min(ans,x+y);
}
cout<<ans<<"\n";
}
return 0;
}
Subtask 5
继续考虑在 Subtask 3 中得到的式子。直接继续推这个式子是推不下去的。但是不难发现,当
因此,可以二分最终的答案
#include<bits/stdc++.h>
using namespace std;
long long n,m;
bool check(long long s){
__int128 x=s/2,y=s-x;
return x*y>=(x+y)*(x+y)-(x+y)*(n+m)+(__int128)n*m;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
int t;cin>>t;
while(t--){
cin>>n>>m;
long long l=1,r=min(n,m);
while(l<r){
long long mid=(l+r)>>1;
if(check(mid)){
r=mid;
}else l=mid+1;
}
cout<<l<<"\n";
}
return 0;
}
Subtask 6
我们可能需要在
目标是要让原式中的
因此,当
考虑回现实情况,
如果
但是,
若
如果
对于下式,其左右侧必然都是整数。因此下式右侧的
(其实不放心的话还可以算出
为了避免精度误差,计算出答案 sqrtl 的复杂度可以看成
我们只证明了答案至少为这个数,要证明一定有对应的方案,还需要将其构造出来。不过不进行构造证明直接输出答案就能获得本题的
#include<bits/stdc++.h>
using namespace std;
long long n,m;
bool check(__int128 s){
__int128 x=s/2,y=s-x;
return x*y>=(x+y)*(x+y)-(x+y)*((__int128)n+m)+(__int128)n*m;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
int t;cin>>t;
while(t--){
cin>>n>>m;
long long ans=ceil(2*(n+m-sqrtl((__int128)n*n+(__int128)m*m-(__int128)n*m))/3);
if(check(ans-1))cout<<ans-1<<"\n";
else if(check(ans))cout<<ans<<"\n";
else cout<<ans+1<<"\n";
}
return 0;
}
这里的 __int128 也可以改成 long double。
附对于 Subtask 1 部分提到的
一些关于这两题的 fun facts:
- 本题来源于我两年前的灵感。当时我画了
10 以内的n=m 的情况,发现了ans=1,2,2,3,4,4,5,6,6,\dots 的规律,即 Subtask 1(不过没发现 Subtask 5 的通用构造方法)。 - 本题的线路不能「绕路」,实际上灵感来源于游戏「跳舞的线」,
其实也有一些对某些城市地铁线路拐来拐去,交而不换的吐槽。 - 这题刚出出来的时候,我认为这题的难度与 CSP-J 2022 T2 相差不大,因为那题也是解一元二次。
- 分成两个 Version 的想法是在组题的时候才有的,原本这题只有 Easy Version。
- Easy Version 样例中的倒数第二组数据是
sqrtl会出现精度误差的数据,最后一组输入有意义且满足ans=n-9999 。