【水题选做】一些简单的数学题

P1487失落的成绩单

题意

就是一个n项数列A满足Ai=Ai1Ai+12+d,给出A1An,求某一项Am的值。

思路

这个式子看起来很好,可惜并不能找到很好的性质,也没有几何意义啥的。

然后我们把它移个项,整理一下,Ai+1+2AiAi1=2d

这是一个常系数线性递推方程,解下特征方程就知道两个特征根分别是x=1±2,特解也很好求,瞪眼法就知道是Ai=d

然后Ai的通解可以表示为C1(1+2)n+C2(12)n+d

代入两个点值,把C1C2解出来就结束了。

这题的坑点在于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个。

每轮队首的人投一次骰子。

16的概率,队首的人获胜。
12的概率,队首的人排到队尾。
13的概率,队首的人出队。

若队列中仅剩一人,则该人获胜,求你获胜的概率。

思路

数据范围n1000,看起来就很像一个Θ(n2)的概率DP。

i个人排第j个赢的概率为dp[i][j],那么当j>1时有dp[i][j]=12dp[i][j1]+13dp[i1][j1]

j==1时有dp[i][1]=16+12dp[i][i]

我们直接把dp[i][i]当成未知量,这样每个值就可以用k1dp[i][i]+k2表示,到最后会得到一个关于dp[i][i]的一元一次方程。

解出dp[i][i],再代回dp[i][1]...dp[i][i1],就得到了n==i时一整行的结果。

输出dp[n][m]即可。为了实现方便,我直接把数对k1,k2存到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算术

题意

算了太长了不想写-_-

思路

显然这个连乘式需要化简。

i=1kpiai+11piai+1piai=i1k11piai+111pi=i=1kj=0ai1pij

观察一下这个东西,对于每个质因子枚举次幂,很像是在枚举因数。我们再乘个n发现式子就变成了因数之和。(把连乘号跟求和号展开写就很明显了)

所以f(n)=σ1(n)n,其中σ1(n)=d|nd,这个式子就很简洁了,但为了求前缀和,我们还是写开。

f(n)=1nd|ndi=1nf(n)=i=1n1id|id

根据套路,枚举因数变为枚举倍数:

i=1nf(n)=d=1ndi=1ni1id=d=1ni=1ni1i

注意这个i=1ni1i

整除分块的形式已经很明显了,因为对于一长串的d,内层求和号的结果不变。

H(n)=i=1n1i

这个东西是调和级数的部分和,在n很大的时候有很好的近似H(n)ln(n)+γγ=0.57722是欧拉常数。

数据小的时候可以预处理,这样H(x)就可以O(1)求解,算上整除分块,时间复杂度为O(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;
}
posted @   文艺平衡树  阅读(123)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示