背景:这是我出的NOIP模拟赛DAY2的T1,因为这道题没有通过审核。
个人觉得这道题虽然有点刁钻,但题目质量还是不错的。
题目:四合一数学
考察知识:数学,数论,计算几何,枚举
算法难度:XXX+ 实现难度:XXX+
评价:sub T1,sub T2 难度正常(甚至偏简单),sub T3,sub T4 难度较大,而且对于NOIP来说考得稍微有点偏。
下面分块讲解:
sub T1:
考察知识:二项式定理 难度:X
由:得到题目中多项式其中一项为:
令:,故答案为:
注意:如果 a+b 不能整除 abn 那么无解
sub T2:
考察知识:数列求和 难度:X+
注意到:
化简得:
注意:化为最简分数
sub T3:
考察知识:计算几何,向量 难度:XXX+
首先,告诉大家一个引理:
对于三角形ABC,其中,那么三角形ABC面积为:
关于引理的证明比较复杂,我也忘记了,就不介绍了。
有了这个公式,我们计算三角形面积就能非常精确了,对于这道题,我们只需要判断一个三角形三个点是否都在另一个三角形里面就可以了。
对于点的判断,我们用三角形面积公式如果P在三角形ABC内部,那么:
sub T4:
母题来源:
没错,全国卷一填空题最后一道!
说明:刚开始我发现最暴力的枚举都可以AC,于是加强了数据(从一组改到最多50组,从三位小数改到四位小数)
其实吧,我并没有把数据添加得非常坑,用我下面的枚举方法是可以AC的。
考察知识:枚举,导数 难度:XXX+
枚举方法:
分析后我们发现,其中一个最大值一定在,所以我们把区间细分,进行枚举,细分不够大会WA,细分太大会TLE。
然后我们继续用我们学过的数学知识分析:的其中一个最大值在,而函数周期为,
所以我们只需要枚举在区间的函数最大值就可以了,还是要细分区间,关键要找对细分区间的大小。
知识拓展(看不看都无所谓):
导数方法:
我们先看2018全国卷一那道题,今年的填空最后一道不难,熟悉导数的同学,应该5分钟之内可以解决吧
解:,考虑 ,
令
当时有最大值,为:,又该函数上界与下界相同,所以其最小值为:
如果你熟悉琴生不等式,这道题可以秒解:
因为在为凸函数,又
由琴生不等式
所以目标最大值为,所以目标最小值为
好了,下面正式讲解导数法:
我们要求目标函数的最大值区间,显然这个区间应该在
然后求导:
令:
然后用和差化积公式: //(说实话,我也记不住和差化积公式)
现在问题来了,当x在区间取多少时函数取最大值呢?
我们发现,随着k的增大,f(x)的图像越来越显得波动,所以面对这道题最暴力的解法就是把区间细分到满足精度的小区间,依次枚举求最值。
但是你会发现这些函数长落是交替的,每一个极值点都对应其一阶导数为0的情况,我们可以考虑枚举使一阶导为0的x的值,即:或: 其中
我们枚举K的值就可以了,这样枚举量就小了很多,毫不费力AC。
部分代码:
//应用namespace分版块解决问题
namespace T1{
long long a,b,n;//防止爆int
void in(){std::cin>>a>>b>>n;}
void solve(){
if((a*b*n)%(a+b)) printf("NA.\n");
else std::cout<<(a*b*n)/(a+b)<<std::endl;
}
}
namespace T2{
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
int n,g;
void in(){scanf("%d",&n);}
void solve(){
int g=gcd(2*n,n+1);
printf("%d/%d\n",2*n/g,(n+1)/g);
}
}
namespace T3{
struct Vct{//向量
int x,y;
void in(){scanf("%d%d",&x,&y);}
friend Vct operator - (Vct A,Vct B){
Vct ret;
ret.x=A.x-B.x,ret.y=A.y-B.y;
return ret;
}
}A1,A2,A3,B1,B2,B3;
int calc(Vct A,Vct B,Vct C){//三角形面积两倍的计算
Vct AB=B-A,AC=C-A;
int ret=abs(AB.x*AC.y-AB.y*AC.x);
return ret;
}
bool is_in(Vct A,Vct B,Vct C,Vct X){//判断点是否在三角形里
int S1=calc(A,B,C),S2=calc(A,B,X)+calc(A,C,X)+calc(B,C,X);
if(S1==S2) return true;
return false;
}
void in(){
A1.in(),A2.in(),A3.in();
B1.in(),B2.in(),B3.in();
}
void solve(){
if(is_in(A1,A2,A3,B1)&&is_in(A1,A2,A3,B2)&&
is_in(A1,A2,A3,B3)) printf("B in A.\n");
else if(is_in(B1,B2,B3,A1)&&is_in(B1,B2,B3,A2)&&
is_in(B1,B2,B3,A3)) printf("A in B.\n");
else printf("Error.\n");
}
}
namespace T4{//方法一:优化后的枚举
const double PI=3.1415926;
int k_[52],T;
void in(){
scanf("%d",&T);
for(int i=1;i<=T;i++) scanf("%d",k_+i);
}
void sub_solve(double k){
double l=PI*(0.5-2.0/k),r=PI*(0.5+2.0/k);
double ans=0.0;
for(double x=l;x<=r;x+=0.000001){
ans=std::max(ans,sin(x)*k+sin(x*k));
}
printf("%.4lf ",ans);
}
void solve(){
for(int i=1;i<=T;i++) sub_solve((double)k_[i]);
printf("\n");
}
}
namespace T4_{//方法二:导数
const double PI=3.1415926;
int k_[52],T;
void in(){
scanf("%d",&T);
for(int i=1;i<=T;i++) scanf("%d",k_+i);
}
void sub_solve(int k){//结论由导数方法推导
double ans=0;
for(int K=0;2*K<k;K++){
double x=PI*(2*K+1)/(k+1);
ans=std::max(ans,sin(x)*k+sin(k*x));
}
for(int K=0;2*K+2<k;K++){
double x=PI*(2*K+1)/(k-1);
ans=std::max(ans,sin(x)*k+sin(k*x));
}
printf("%.4lf ",ans);
}
void solve(){
for(int i=1;i<=T;i++) sub_solve(k_[i]);
printf("\n");
}
}