zoukankan      html  css  js  c++  java
  • 【清北学堂2018刷题冲刺】Contest 1

    Task 1:最小公倍数

     输入n,求n与246913578的最小公倍数。

     结果对1234567890取模。

    【样例输入】

    3
    

    【样例输出】

    246913578
    

    【数据规模和约定】

    • 对于30%的数据, n<=10^9
    • 对于100%的数据,n<=10^18
    • 对于100%的数据,n<=10^100000

    80分算法:

     可以得到246913578和1234567890具有倍数关系,所以答案只有5种:

    • 246913578*{0,1,2,3,4}

     求最小公倍数可以转化为求最大公约数,也就是:

    • (n*246913578%gcd(n,246913578))%1234567890

     因为乘法会爆longlong,所以我们可以把它转成:

    • 246913578*((n/gcd(n,246913578))%5)

     对解决不了的高精度部分做一个特判0/5处理,再加上一个随机化骗分,80就到手了qwq

    正解:

     对于这个高精度数,可以认为它是通过多位数字构造组合起来的数,满足加法原理和乘法原理。

     所以我们只需要每一次获取这个数的一位并取模就可以得到模意义下的这个数,在数字构造的时候直接求gcd取模即可


    Task 2:不可逆转

     求有多少1~n的排列满足:这个排列是波动的。

     用a[i]表示排列中的第i个数,波动的意思是,对任意1<=i<=n-2,

     若a[i]<a[i+1],则a[i+1]>a[i+2]

     若a[i]>a[i+1],则a[i+1]<a[i+2]

     答案对m取模

    【样例输入】

    3 15
    

    【样例输出】

    4
    

    【数据规模和约定】

    • 对于30%的数据, n<=10
    • 对于60%的数据, n<=100
    • 对于100%的数据, n<=1000,m<=10^9

     这个题目实际上是山东省选的一个原题,原题叫做地精部落,实际上就是求波动数列的个数。

     在原来的题解里面我见过一些非常玄学的解法,实在想不明白,直到看到这个题目的讲解才明白这个题目有更好的想法。

    大致思路是这样的:

    • 设计一个状态f[i]表示1-i的排列里面波动数列的个数,然后取出i+1向原数列中插入。
    • 由于上升和下降本质一直,可以对称得到所以只求一个方向就可以。
    • 由于每个数不同,最大值左右两边数列(大小为k-1和n-k)可以离散化为[1,k-1][1,n-k]两个已解决数列。
    • 对一个确定的数列方案数为f[k-1]*f[ n-k ]。其间数字可以互换,考虑在n-1个数里拆出这两个数列的方案数,为C(k-1,n-1)。
    • 可以得到对于每一个位置k,方案数为f[k-1]*f[n-k]*C(k-1,n-1),总方案数就是f[n]=Σ(k为奇数/偶数)f[k-1]*f[n-k]*C(k-1,n-1)了

    嗯,写起来相当简单,但思考起来就不是了。


    Task 3:数值微分

    【问题描述】

     在数学中,对光滑函数求微分是一种常见的操作。在实际应用中,一些函数没有解析形式,通常会从函数上取若干个点,用这些点来近似地表示这个函数。

     现在有一个函数f(x),我们在函数上取n个值f(1),f(2),…,f(n)。对函数f(x)取微分得到函数f’(x)。我们近似地认为f’(i)=f(i)-f(i-1)。

     同理,对f’(x)求微分可以得到f’’(x),我们近似地认为f’’(i)=f’(i)-f’(i-1)。(注意这里的f’(i)和f’(i-1)本身就是我们求的近似值)。

     函数f’(x)被称为一阶微分,f’’(x)被称为二阶微分。如果对函数f(x)连续做m次微分操作,得到的函数被称为m阶微分。特殊地,f(x)可以被认为是自身的0阶微分。

     用f[m](x)表示f(x)的m阶微分,我们认为对任意自然数m,有f[m](0)=0。在计算近似值时,直接使用这条性质。

     现在,给出f(1),f(2),…,f(n),以及m。,求f[m](1), f[m](2),…, f[m](n)。

     把上面所说的内容说的清楚一点,输入的是f(1),f(2),…,f(n):

    • 令f[0](x)=f(x),x=1,2,…,n
    • 令f[i](0)=0,i=0,1,…m
    • 令f[i](x)=f[i-1](x)-f[i-1](x-1), x=1,2,…,n, i=1,2,…,m
    • 输出的是f[m](x) ,x=1,2,…,n

    【输入格式】

    • 第一行两个数n,m

    • 第二行n个数f(1),f(2),…,f(n)

    【输出格式】

    • 输出n行,第x行是f、m

    • 结果对100007取模

    【样例输入】

    3 2
    6 7 8
    

    【样例输出】

    6
    100002
    0
    

    【数据规模和约定】

    • 对于30%的数据, m<=1000
    • 对于60%的数据, m<=10^6
    • 对于100%的数据, n<=1000,m<=10^9,0<=f(i)<100007

     题目意思其实蛮简单的,就是求f[ i ][ j ]=f[ i-1 ][ j ]-f[ i-1 ][ j-1 ],一个类似于杨辉三角的递推。

     经过手推我们会发现其实所谓m阶微分就是(a-b)m的多项式展开,也就是求Σ(-1)i*C( m , i )。

     想到这里就不难处理了,但是紧接着的问题还有一个,就是应该怎么求组合数。

     对于这个题目,m和n的范围差距悬殊,而且无论怎么样单次计算组合数都会超时。考虑到组合数较大项一定为m,较小项范围为[ 1 , n ],且n的范围为1000,m的范围为1000000000,我们需要用一种高效的求法。

     先复习一下组合数的求法有哪些吧。

    •   硬算 单次O( max( n , m ) )
    •   阶乘逆元求法 适用于多次查询,复杂度预处理O( nlogn ),查询接近O( 1 ),但是模数必须是质数。(100007不是质数)
    •   杨辉三角递推求法 适用于多次密集小范围查询,复杂度预处理O( n^2 ),查询O( 1 )
    •   费马小定理/exgcd求逆元 单次O( n+logn ),是最简单的方法,模数必须为质数
    •   阶乘分解求逆元 适用于高精度组合数计算,或者模数不为质数的求法。复杂度预处理O( nlogn ),查询O( 1 )

     虽然写起来稍微有点麻烦,但阶乘分解确实是唯一可行的方法了。所以基础打牢还是相当重要的啊QWQ

  • 相关阅读:
    NHibernate介绍
    dwr配置文件dwr.xml详解
    架构设计师与SOA
    SOA是什么
    JDK常用命令
    在WPF的WebBrowser控件中抑制脚本错误
    通过编程计算一个游戏的胜率
    在C#中模拟大数乘法
    解决HttpWebRequest首次连接特别慢的问题
    布隆过滤器(Bloom Filter)
  • 原文地址:https://www.cnblogs.com/maomao9173/p/9790423.html
Copyright © 2011-2022 走看看