2016年ACM/ICPC大連賽區重現賽 F題
本題簡單明瞭
給你 T 組資料,然後輸入 n ,把n拆解成不同的數字,使這些數字相乘最大。
。。。
這種簡單題往往最噁心。。
首先我們想一下,如果把一個數拆分成任意的數字相乘,最大的肯定是 2*2*2*2*2*。。。。到最後剩下奇數就*3,但是這道題是不同的數字,就往儘可能 小數多乘。這種思路走。。
那麼這道題就顯而易見了
拆解成 2+3+4+5+......+x , 假設 設此時 和為 Sn
那麼有以下幾種情況
Sn == n 輸出 x! 完美解決
Sn == n+1 那麼 我們捨棄 2 留下 x+1 就好了
如果 Sn > n + 1 那麼 Sn - n = k 捨棄掉 k ,留下 x+k-1 就解決了。。
但是問題關鍵來了。。。當初自己天真的去解決這個問題時候,發現如果加入取模運算,就沒那麼簡單了,階乘的話進行一下打表處理就可以了,但是這道題 需要一下逆元處理。。。這就有些超出理解認知了。。。還需要學習啊。。。
但是看了看大犇們的部落格。。。發現自己思路一樣以外。。。逆元根本不懂。。
以下摘錄自一個大犇的部落格:http://www.cnblogs.com/Judge/p/9383034.html
所以逆元是什麼東西呢? 首先這裡有個式子: (a/b) %p ,這個式子的答案怎麼求?
沒錯,暴力求是一種方法,但是當 b 非常大的時候呢 ? 這個時候就要用到逆元了。--來自網路上大犇的部落格
我們不能直接說一個數的逆元是多少,
應該這麼說: 一個數 x 在模 p 的條件下的逆元是多少
其次,我們不難得知一個數的逆元有多個,但是我們只需要求得一個數的最小正整數逆元就行了
考慮到這道題數比較大,所以需要逆元處理 那麼 除以一個數再取模等同於乘以這個數的逆元再取模。。
只能膜拜大犇了。。。這次又學習了逆元,也算有收穫了。
下邊是個 AC 程式碼。。我盡力的去搞一下逆元吧。。(注意HDU 上提交用 G++ C++超時。。。)
#include<iostream> #include<cstdio> using namespace std; #define ll long long int const ll mod = 1e9 + 7; const ll maxn = 100010; ll n; ll temp,ant; bool jud(ll x) { return ((x*(x+1)/2-1)>=n); } ll pow_(ll x,ll n) { ll ans = 1; x=x%mod; while(n) { if(n&1) ans=ans*x%mod; x=x*x%mod; n>>=1; } return ans; } ll jie[maxn]; int main() { jie[0]=1; for(int i=1;i<maxn;i++) jie[i]=(jie[i-1]*i)%mod; int t; scanf("%d",&t); while(t--) { scanf("%lld",&n); if(n<=4) { printf("%lld\n",n); continue; } ll l=0,r=n,mid; while(l<r) { mid = (l+r)>>1; if(jud(mid)) r=mid; else l=mid+1; } l=r; ant = 1; temp = (r*(r+1)/2-1); if(temp==n) { printf("%lld\n",jie[l]); continue; } l--; if(temp == n+1) { ant=jie[l]; ant=ant*pow_(2,mod-2)%mod; ant=(ant*(l+2))%mod; printf("%lld\n",ant); } else { ant=jie[l+1]; ant=ant*pow_(temp-n,mod-2)%mod; printf("%lld\n",ant); } } return 0; }