清北学堂2018国庆训练(北京)Contest 1

    在我意识到这几天的学习很有必要写一下解题报告的时候,实际上比赛已经进行5场了。现在来补一下,可能不会更新太快,但是我会尽可能去跟上进度。


 Task 1:最小公倍数
【问题描述】
    输入n,求n与246913578的最小公倍数。
    结果对1234567890取模。
【输入格式】
    一个正整数n
【输出格式】
    最小公倍数
【样例输入】
    3
【样例输出】
    246913578
【数据规模和约定】
    对于30%的数据, n<=10^{9}
    对于60%的数据,n<=10^{18}
    对于100%的数据,n<=10^{100000}

    对于这道题目,很显而易见的,我们可以得到246913578和1234567890具有倍数关系。所以就答案而言,只有246913578*(0,1,2,3,4)这5种。

    求最小公倍数可以转化为求最大公约数,也就是(n*246913578%gcd(n,246913578))%1234567890。因为乘法会爆longlong,所以我们可以把它转成246913578*((n/gcd(n,246913578))%5)。对解决不了的高精度部分做一个特判0/5处理,再加上一个随机化骗分,80就到手了qwq

    实际上正解也非常简单,思路上比较巧妙。对于这个高精度数可以认为它是用乘法原理和加法原理组合起来的数,所以我们只需要每一次获取这个数的一位并取模就可以得到模意义下的这个数。

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取模
【输入格式】
    一行两个数n,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](x)。    
    结果对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

猜你喜欢

转载自www.cnblogs.com/maomao9173/p/9790288.html