【水题选做】一些简单的数学题
作者:互联网
P1487失落的成绩单
题意
就是一个\(n\)项数列\(A\)满足\(A_i=\frac{A_{i-1}-A_{i+1}}{2}+d\),给出\(A_1\)和\(A_n\),求某一项\(A_m\)的值。
思路
这个式子看起来很好,可惜并不能找到很好的性质,也没有几何意义啥的。
然后我们把它移个项,整理一下,\(A_{i+1}+2A_i-A_{i-1}=2d\)
这是一个常系数线性递推方程,解下特征方程就知道两个特征根分别是\(x=-1\pm\sqrt{2}\),特解也很好求,瞪眼法就知道是\(A_i=d\)
然后\(A_i\)的通解可以表示为\(C_1*(-1+\sqrt{2})^n+C_2*(-1-\sqrt{2})^n+d\)
代入两个点值,把\(C_1\)和\(C_2\)解出来就结束了。
这题的坑点在于\(m\)可能\(=0\),要特判输出\(0\)。
代码
点击查看代码
#include<cstdio>
#include<cstdlib>
#include<cmath>
#define db double
using namespace std;
int main(){
int n,m,i,j;
db d,A1,An;
db C1,C2;
db k1,k2,k3,k4,b1,b2;
scanf("%d%d",&n,&m);
scanf("%lf%lf%lf",&d,&A1,&An);
if(m==0){
printf("0.000");
return 0;
}
k1=sqrt(2)-1,k2=-1-sqrt(2);
k3=pow(k1,n),k4=pow(k2,n);
b1=A1-d,b2=An-d;
C2=(b2*k1-b1*k3)/(k1*k4-k2*k3);
C1=(b1-C2*k2)/k1;
db ans=C1*pow(k1,m)+C2*pow(k2,m)+d;
printf("%.3lf",ans);
// system("pause");
return 0;
}
P1409 骰子
题意
\(n\)个人排成一排,你排在第\(m\)个。
每轮队首的人投一次骰子。
\(\frac{1}{6}\)的概率,队首的人获胜。
\(\frac{1}{2}\)的概率,队首的人排到队尾。
\(\frac{1}{3}\)的概率,队首的人出队。
若队列中仅剩一人,则该人获胜,求你获胜的概率。
思路
数据范围\(n\leq 1000\),看起来就很像一个\(\Theta(n^2)\)的概率DP。
设\(i\)个人排第\(j\)个赢的概率为\(dp[i][j]\),那么当\(j>1\)时有\(dp[i][j]=\frac{1}{2} dp[i][j-1]+\frac{1}{3}dp[i-1][j-1]\)。
\(j==1\)时有\(dp[i][1]=\frac{1}{6}+\frac{1}{2}dp[i][i]\)。
我们直接把dp[i][i]当成未知量,这样每个值就可以用\(k_1dp[i][i]+k_2\)表示,到最后会得到一个关于\(dp[i][i]\)的一元一次方程。
解出\(dp[i][i]\),再代回\(dp[i][1]...dp[i][i-1]\),就得到了\(n==i\)时一整行的结果。
输出\(dp[n][m]\)即可。为了实现方便,我直接把数对\(k_1,k_2\)存到dp数组里。
代码
点击查看代码
#include<cstdio>
#include<cstdlib>
#define db double
#define maxn 1010
using namespace std;
struct data{
db x,y;
data(){x=y=0;}
data(db t1,db t2){
x=t1;y=t2;
}
data operator *(db t){
return data(t*x,t*y);
}
data operator +(data t){
return data(x+t.x,y+t.y);
}
} dp[maxn][maxn];
int main(){
int i,j,n,m;
db tmp;
scanf("%d%d",&n,&m);
dp[1][1]=data(0,1);
for(i=2;i<=n;++i){
dp[i][1]=data(0.5,1.0/6.0);
for(j=2;j<=i;++j){
dp[i][j]=dp[i][j-1]*(1.0/2.0)+dp[i-1][j-1]*(1.0/3.0);
}
tmp=dp[i][i].y/(1-dp[i][i].x);
dp[i][i]=data(0,tmp);
for(j=1;j<i;++j){
db t1,t2;
t1=dp[i][j].x;t2=dp[i][j].y;
dp[i][j]=data(0,t1*tmp+t2);
}
}
printf("%.9lf",dp[n][m].y);
// system("pause");
return 0;
}
P6028算术
题意
算了太长了不想写-_-
思路
显然这个连乘式需要化简。
\[\prod_{i=1}^{k}\frac{p_i^{a_i+1}-1}{p_i^{a_i+1}-p_i^{a_i}}=\prod_{i-1}^{k}\frac{1-\frac{1}{p_i^{a_i+1}}}{1-\frac{1}{p_i}}=\prod_{i=1}^k\sum_{j=0}^{a_i}\frac{1}{p_i^j} \]观察一下这个东西,对于每个质因子枚举次幂,很像是在枚举因数。我们再乘个\(n\)发现式子就变成了因数之和。(把连乘号跟求和号展开写就很明显了)
所以\(f(n)=\frac{\sigma_1(n)}{n}\),其中\(\sigma_1(n)=\sum_{d|n} d\),这个式子就很简洁了,但为了求前缀和,我们还是写开。
\(f(n)=\frac{1}{n}\sum_{d|n}d\),\(\sum_{i=1}^n f(n)=\sum_{i=1}^{n}\frac{1}{i}\sum_{d|i} d\)
根据套路,枚举因数变为枚举倍数:
\[\sum_{i=1}^nf(n)=\sum_{d=1}^nd\sum_{i=1}^{\lfloor\frac{n}{i}\rfloor}\frac{1}{id}=\sum_{d=1}^n\sum_{i=1}^{\lfloor\frac{n}{i}\rfloor}\frac{1}{i} \]注意这个$$\sum_{i=1}^{\lfloor\frac{n}{i}\rfloor}\frac{1}{i}$$
整除分块的形式已经很明显了,因为对于一长串的\(d\),内层求和号的结果不变。
令\(H(n)=\sum_{i=1}^{n}\frac{1}{i}\)
这个东西是调和级数的部分和,在\(n\)很大的时候有很好的近似\(H(n)\approx ln(n)+\gamma\),\(\gamma=0.57722\)是欧拉常数。
数据小的时候可以预处理,这样\(H(x)\)就可以\(O(1)\)求解,算上整除分块,时间复杂度为\(O(\sqrt n)\)。
代码
点击查看代码
#include<cstdio>
#include<cstdlib>
#include<cmath>
#define db long double
#define ll long long
#define gamma 0.57722
using namespace std;
db f[10000010]={0};
db F(ll n){
if(n<=10000000) return f[n];
else return log(n)+gamma;
}
int main(){
int i,j;
ll l,r,n;
db ans=0;
for(i=1;i<=10000000;++i) f[i]=f[i-1]+(db)1.0/(db)i;
scanf("%lld",&n);
for(l=1,r=1;l<=r;l=r+1,r=n/(n/l)){
ans+=(r-l+1)*F(n/l);
if(r>=n) break;
}
printf("%.10Lf",ans);
// system("pause");
return 0;
}
标签:frac,水题,sum,db,简单,数学题,include,data,dp 来源: https://www.cnblogs.com/landmine-sweeper/p/16225929.html