1. 程式人生 > >清北學堂2018國慶訓練(北京)Contest 1

清北學堂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