【JZOJ 3885】 搞笑的代码

Description

在OI界存在着一位传奇选手——QQ,他总是以风格迥异的搞笑代码受世人围观
某次某道题目的输入是一个排列,他使用了以下伪代码来生成数据
while 序列长度< n do
{
随机生成一个整数属亍[1,n]
如果这个数没有出现过则加入序列尾
}
聪明的同学一定发现了,这样生成数据是徆慢的,那么请你告诉QQ,生成一个n排列的期望随机次数
100%数据满足n≤2^31

Analysis

题目转化

解一个方程, 就能知道答案为

i=1nni

solution 1 打表

当然不是把每个答案都打出来,首先打的是 ni=11i 的表,这样才能一直算下去,但是空间会爆
然后有一个很劲的技巧,每隔10000000打一个数,那么各个答案之间只差一千万,可以直接算,而且空间又不会爆,很好的平衡了时空复杂度

solution 2 调和级数

这个答案显然是调和级数h(n)*n

性质
当 n 很大的时候,h(n)与ln(n)的差约等于欧拉常数,不过 n 较小的时候误差较大

所以当n较小的时候O(n)算,n较大时直接用(ln(n)+euler)*n就好了
PS:这个方法谁 TM 比赛时想得到啊
打表大法好!打表大法好!打表大法好!

Code

#include<cstdio>
#include<cmath>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef double db;
const db euler=0.57721566490153286060651209;
int main()
{
    int n;
    scanf("%d",&n);
    if(n>=50000000) printf("%.lf",(log(n*1.0)+euler)*n);
    else
    {
        db ans=0;
        fo(i,1,n) ans+=n*1.0/i;
        printf("%.lf",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值