1.信仰之跃
如图,一名刺客怀着信条,要进行他的信仰之跃。下面有个草垛,他必须跳到草垛里,不然会摔死。刺客可以看成一个质点,给出高度h和草垛距离前方刺客距离d的范围,请你计算他的初速度v(水平方向)的范围。(刺客在M星球,M星球的重力加速度g为18m/s^2)
图片来源:平抛运动8个公式是什么?
本题中只需要用到竖直位移和水平位移。对于本题来说,重要的是分数的表示。
分数的表示,参考:C++ 关于分数的处理与计算
其主要思想是用一个类表示分数,一个属性表示分子,另一个属性表示分母。再进行加减乘除。
/*
* @Author: error: git config user.name && git config user.email & please set dead value or install git
* @Date: 2022-08-05 15:15:22
* @LastEditors: error: git config user.name && git config user.email & please set dead value or install git
* @LastEditTime: 2022-08-05 18:30:35
* @FilePath: \C++\testC++\JumpWall.cpp
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
#include<iostream>
#include<cmath>
#include<vector>
using namespace std;
const int g=18;
class Assassin{
private:
int x1;//水平方向区间的开始
int x2;//区间结束
int y;//垂直方向
public:
Assassin(int x1,int x2,int y);
int X1Get();
void X1Set(int x1);
int X2Get();
void X2Set(int x2);
int YGet();
void YSet(int y);
~Assassin(){};
};
struct Fraction{
int up;//分子
int down;//分母
Fraction(){};
Fraction(int _up,int _down)
{
this->up = _up;
this->down = _down;
}
};
Fraction f1;
Fraction f2;
vector<Fraction> v1;//存放区间
//辗转相除法,递归,导致时间复杂度太大
int gcd(int a,int b)
{
if(b==0)
{
return a;
}
else
{
return gcd(b,a%b);
}
}
//分数的化简
Fraction reduction(Fraction result)
{
if(result.down < 0)
{
result.up *= (-1);
result.down *= (-1);
}
if(result.up == 0)
result.down = 1;
int _gcd = gcd(abs(result.up),abs(result.down));
result.up = result.up/_gcd;
result.down = result.down/_gcd;
return result;
}
Assassin::Assassin(int x1,int x2,int y)
{
this->x1=x1;
this->x2=x2;
this->y=y;
}
int Assassin::X1Get()
{
return x1;
}
void Assassin::X1Set(int x1)
{
this->x1=x1;
}
int Assassin::X2Get()
{
return x2;
}
void Assassin::X2Set(int x2)
{
this->x2=x2;
}
int Assassin::YGet()
{
return y;
}
void Assassin::YSet(int y)
{
this->y=y;
}
Fraction divide(Fraction f1 , Fraction f2)
{
Fraction f3;
if(f2.up == 0)
return f2;//分母为0
f3.up = f1.up*f2.down;
f3.down = f1.down*f2.up;
f3 = reduction(f3);
return f3;
}
void print(Fraction f1,Fraction f2)
{
if(f1.down==1 && f2.down==1)//如果分母是1,则当做整数输出
{
cout<<f1.up<<" "<<f2.up<<endl;
}
else if(f1.down==1 && f2.down>1)
{
cout<<f1.up<<" "<<f2.up<<"/"<<f2.down<<endl;
}
else if(f1.down>1 && f2.down==1)
{
cout<<f1.up<<"/"<<f1.down<<" "<<f2.up<<endl;
}
else
{
cout<<f1.up<<"/"<<f1.down<<" "<<f2.up<<"/"<<f2.down<<endl;
}
}
void CalV(int &d1,int &d2,int &h)
{
Assassin assassin(d1,d2,h);
Fraction t;//表示t
//根据y=1/2gt^2,求t,t可能为分数
//int t=int(sqrt(2*assassin.YGet()/g));
t.up=int(sqrt(assassin.YGet()));
t.down=sqrt(g/2);
t=reduction(t);//化简
//根据x=vt,求v,v可能为分数
//将f1(x1)看做一个分数
f1.up=assassin.X1Get();
f1.down=1;
//v=x/t
f1=divide(f1,t);
f1=reduction(f1);//约分
v1.push_back(f1);
//f2(x2)
f2.up=assassin.X2Get();
f2.down=1;
f2=divide(f2,t);
f2=reduction(f2);//约分
v1.push_back(f2);
}
int main()
{
int T;
cin>>T;
int h,d1,d2;
for(int i=0;i<T;++i)
{
cin>>h>>d1>>d2;
CalV(d1,d2,h);
}
//正向输出所有元素
vector<Fraction>::iterator it;
for(it=v1.begin();it!=v1.end();it+=2)
{
print(*it,*(it+1));
}
return 0;
}
2.互质
输入两个正整数n和k,求与n互质的第k个正整数。
/*
* @Author: error: git config user.name && git config user.email & please set dead value or install git
* @Date: 2022-08-04 16:47:37
* @LastEditors: error: git config user.name && git config user.email & please set dead value or install git
* @LastEditTime: 2022-08-04 23:28:24
* @FilePath: \C++\testC++\PrimeNumber.cpp
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
#include<iostream>
using namespace std;
const int maxn=1000006;
int prime[maxn],num=0;
/*
采用欧拉函数进行求取
对正整数n,欧拉函数是小于等于n的正整数中与n互质的数的数目.
欧拉函数Euler(n):求[2,n]中有多少个数与n互素
直接利用公式:φ(x)=x(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)…..(1-1/pn)
其中:
pi为x的素因数
每个素因数只用一次
比如90=2 * 3^2 * 5
φ(90) = 90 * (1-1/2) * (1-1/3) * (1-1/5)
φ(1)=1(唯一和1互质的数(小于等于1)就是1本身)
如果不同的整数X和Y的最⼤公约数为1,则称X和Y为互质数。
Y的最⼤公约数为1,则称X和Y为互质数。显然,X和Y互质等价于Y和X互质。给出整数N,求1~N之间,有多少个数对
(X,Y)是互质数。
例如N=5时,(1,2) , (1,3), (1,4), (1,5), (2,3), (2,5), (3,4), (3,5), (4,5)共9对数是互质数。
*/
//辗转相除法,递归,导致时间复杂度太大
int gcd(int a,int b)
{
if(b==0)
{
return a;
}
else
{
return gcd(b,a%b);
}
}
/*
由于当a与b互质时,a+b与b互质,可以求出a以内的互质的数,
由周期性推出剩下所有的与a互质的数
*/
int FindK(int n,int k)
{
for(int i=1;i<n;i++)
{
if(gcd(n,i)==1)
{
prime[++num]=i;
}
}
return k/num*n+prime[k%num];
}
int main()
{
int n,k;
cin>>n>>k;
int result=FindK(n,k);
cout<<result;
return 0;
}
参考:洛谷P1592 互质
3.求解同余方程
对于同余方程,求解它的最小整数解x0
方案:采用扩展欧几里得,即辗转相除法
/*
* @Author: error: git config user.name && git config user.email & please set dead value or install git
* @Date: 2022-08-05 09:42:32
* @LastEditors: error: git config user.name && git config user.email & please set dead value or install git
* @LastEditTime: 2022-08-05 10:31:03
* @FilePath: \C++\testC++\congruentEquation.cpp
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
#include<iostream>
using namespace std;
/*
扩展欧几里得是用来求这样的一组解的:ax + by = gcd(a, b),求x和y,
(求出x后自然知道了y,所以算是求一个x)
逆元呢是求这样的一个解:ax ≡ 1 (mod b),(为了方便理解,改了一下变量名),
求x,貌似并没有一点点相似处,那么我们变一下,
ax+by = gcd(a,b),变成 ax+by = c;
ax ≡ 1 (mod b),变成 ax-by = 1;如果将y看成负的,ax+by = 1;
完全一样,所以直接套用扩展欧几里得求
*/
int exgcd(int a,int b,int &x,int &y)
{
//gcd(a,b)表示a和b的最大公约数,0与一个数的最大公约数是这个数本身
//此时,毫无疑问gcd(a,b) == a,此时存在解x = 1,y = 0;
if(b==0)
{
y=0;
x=1;
return a;
}
int r=exgcd(b,a%b,x,y);
/*
'''
b*x1 + (a%b)*y1 = gcd
由 a%b = a - (a/b)*b(这里的 “/” 指的是整除,例如 5/2=2 , 1/3=0)可得
gcd = b*x1 + (a-(a/b)*b)*y1
= a*y1 + b*(x1 – a/b*y1)
所以
x = y1
y = x1 – a/b*y1
'''
*/
int temp=x;
x=y;
y=temp-a/b*y;
return r;
}
int main()
{
int a,b,x,y;
cin>>a>>b;
int num=exgcd(a,b,x,y);
cout<<(x%b+b)%b<<endl;
return 0;
}
4.新世纪线段覆盖
现给定一根坐标轴,坐标轴上有n条线段,每条线段从1开始编号,若线段编号为k,则该线段覆盖的区间是:[(k-1)×m,(k-1)×m+l]。
有一个质点从原点开始向x轴正半轴移动,每次移动d距离。问第一个不被线段覆盖的点的位置在哪里?
对于该题来说,暴力解法的时间复杂度相当高,因此采用区间求解。
以样例1为例:此时n=5,d=4,m=11,l=8;
对于1到n的区间内,从1开始遍历到n,
当k=1时,[(k-1)*m,(k-1)*m+l]为[0,8];
当k=2时,[(k-1)*m,(k-1)*m+l]为[11,19];
当k=3时,[(k-1)*m,(k-1)*m+l]为[22,30];
…
因此,只需要判断(8,11)(19,22)之间的数是否是d的倍数(注意,这里是开区间),仅且判断一次,如果是,这直接跳出循环,返回数字。
当超出n的时候,例如n=2,d=4,m=11,l=8;
则用(n-1)*m+l来对d取余,设j=((n-1)*m+l)%d,那么还差d-j个数便是下一个d的倍数。
代码如下:
/*
* @Author: error: git config user.name && git config user.email & please set dead value or install git
* @Date: 2022-08-05 19:04:25
* @LastEditors: error: git config user.name && git config user.email & please set dead value or install git
* @LastEditTime: 2022-08-05 20:05:51
* @FilePath: \C++\testC++\LineCoverage.cpp
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
#include<iostream>
using namespace std;
/*
每次移动d,记录x当前的位置
如果采用暴力法,时间复杂度很高
*/
int LineCoverage(int n,int d,int m,int l)
{
if(n==1)
{
//如果超出n条线段的最大区间
int num=((n-1)*m+l)%d;
return ((n-1)*m+l)+(d-num);
}
else if(n>1)
{
//当n>1时
for (int i=1;i<n;i++)
{
//在区间((i-1)*m+l)--((i+1-1)*m)中的数,是否是d的倍数
//如果是,则寻找超出((i-1)*m+l)的第一个数
for(int k=((i-1)*m+l)+1;k<((i+1-1)*m);k++)
{
if(k%d==0)
{
return k;
}
}
}
//如果超出n条线段的最大区间
int num=((n-1)*m+l)%d;
return ((n-1)*m+l)+(d-num);
}
return 0;
}
int main()
{
int n,d,m,l;
cin>>n>>d>>m>>l;
int result=LineCoverage(n,d,m,l);
cout<<result<<endl;
return 0;
}
在提交时,有一处错误,应该是有种情况没有考虑到
经过反复检查发现当n,d,m,l取1e6时,超出int范围,导致错误发生,因此修改变量类型,改为long long ,代码如下:
/*
* @Author: error: git config user.name && git config user.email & please set dead value or install git
* @Date: 2022-08-05 19:04:25
* @LastEditors: error: git config user.name && git config user.email & please set dead value or install git
* @LastEditTime: 2022-08-05 22:13:14
* @FilePath: \C++\testC++\LineCoverage.cpp
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
#include<iostream>
using namespace std;
/*
每次移动d,记录x当前的位置
如果采用暴力法,时间复杂度很高
*/
long long LineCoverage(long long n,long long d,long long m,long long l)
{
if(n==1)
{
//如果超出n条线段的最大区间
int num=((n-1)*m+l)%d;
return ((n-1)*m+l)+(d-num);
}
else if(n>1)
{
//当n>1时
for (int i=1;i<n;i++)
{
//在区间((i-1)*m+l)--((i+1-1)*m)中的数,是否是d的倍数
//如果是,则寻找超出((i-1)*m+l)的第一个数
for(long long k=((i-1)*m+l)+1;k<((i+1-1)*m);k++)
{
if(k%d==0)
{
return k;
}
}
}
//如果超出n条线段的最大区间
int num=((n-1)*m+l)%d;
return ((n-1)*m+l)+(d-num);
}
return 0;
}
int main()
{
long long n,d,m,l;
cin>>n>>d>>m>>l;
long long result=LineCoverage(n,d,m,l);
cout<<result<<endl;
return 0;
}