1. 程式人生 > >2016 ACM/ICPC亞洲區大連站 F - Detachment 【維護前綴積、前綴和、二分搜索優化】

2016 ACM/ICPC亞洲區大連站 F - Detachment 【維護前綴積、前綴和、二分搜索優化】

case 兩個 http equal only 下一個 每一個 oci thml

F - Detachment

In a highly developed alien society, the habitats are almost infinite dimensional space.
In the history of this planet,there is an old puzzle.
You have a line segment with x units’ length representing one dimension.The line segment can be split into a number of small line segments: a1,a2a1,a2, … (x=
a1+a2a1+a2+…) assigned to different dimensions. And then, the multidimensional space has been established. Now there are two requirements for this space:
1.Two different small line segments cannot be equal ( aiajai≠aj when i≠j).
2.Make this multidimensional space size s as large as possible (s= a1?a2a1?a2*...).Note that it allows to keep one dimension.That‘s to say, the number of ai can be only one.
Now can you solve this question and find the maximum size of the space?(For the final number is too large,your answer will be modulo 10^9+7)

InputThe first line is an integer T,meaning the number of test cases.
Then T lines follow. Each line contains one integer x.
1≤T≤10^6, 1≤x≤10^9OutputMaximum s you can get modulo 10^9+7. Note that we wants to be greatest product before modulo 10^9+7.

Sample Input

1
4

Sample Output

4

題意概括:

給一個數 N ,可以把它分解成若幹個不相同的數 N = a1+a2+a3+...... ,求最大的 S = a1*a2*a3*.......

分多少個隨意,也可以不分。

解題思路:

一開始以為是規律題,

後來推了無果,筆紙模擬到 11 發現一個神奇的貪心

要是乘積最大化,那可分的數越多越好。

而每一個數可分的數字數量是有規律的。

x1 = 1, s1 = 1;

x2 = 2, s2 = 2;

x3 = 3, s3 = 3;

x4 = 4, s4 = 4;

x5 = 2+3, s5 = 2*3;

x6 = 2+4 [2+(3+1)], s6 = 2*4;

x7 = 3+4 [(2+1) + (3+1)], s7 = 3*4;

x8 = 3+5 [(2+1) + (3+1+1)], s8 = 3*5;

x9 = 2+3+4, s9 = 2*3*4;

x10 = 2+3+5 [2+3+(4+1)], s10 = 2*3*5;

x11 = 2+4+5 [2+(3+1)+(4+1)], s11 = 2*4*5;

...

如何構造貪心顯而易見了,

假如有一個數讓你拆成兩個要求積最大,肯定是拆成兩個一樣的,如果拆成n個,肯定就是拆成這個數/n,如果沒說幾個,肯定都拆成幾個3就拆成幾個三,剩下的拆成2.這個題要求不能一樣的,所以肯定就是2,3,4這樣排列了,從2開始讓它變小,這樣數量會最多,每次加一就是讓他越來越接近。所以用前綴和記錄,如果有剩余的話,肯定是從後往前逐個+1,而且剩余的那個數最多=2,3,4...最大的那個數,舉個例子會發現有兩種情況:1.比如2*3*4*5余下5,相等。最優就是3*4*5*7,就是每個數都加一遍,然後會剩下一個1,加給最後一個,總的來說就是 除以2,乘上t+2,(t是最後一個數的值);2.不相等,2,3,4,5,余2,最優就是2,3,5,6,就是用3的階乘*6的階乘除以4的階乘,因為數量太多,不能一個一個乘,就用前綴積相除,數字太大,就要用逆元了。。然後用二分優化下。。

數據量大,要用前綴積+逆元求,用前綴和優化,二分找小於當前值的最接近的前綴和,求余數

參考一篇大牛的證明:

https://blog.csdn.net/qq_34374664/article/details/53466435

此題關鍵在於得出如何能使乘積s最大

按照以往經驗,必然是取一段連續自然數能夠使得乘積最大,而這段連續自然數可從2開始(為啥不從1開始?從1開始還不如將這個1給這段連續自然數的最後一個數),

於是我們可以得到形如2+3+4+...+k(k=2,3,...)的式子,而x是10^9內的任意整數,我們不可能恰好能夠湊成連續自然數之和,可能會多出△x

而這個△x的值,我可以保證它的範圍為0≤△x≤k,相信大於等於0還是好理解的,為什麽會小於等於k呢?因為當它大於k時,原式不是可以增加一項?即2+3+4+...+k+(k+1)

那麽多出來的△x怎麽處理呢?顯然是從後往前均攤給連續自然數中的(k-1)個數,為啥從後往前?因為若我們從前往後,總是會使連續自然數重復,不好處理

於是,在我們分配完△x之後,我們大致會得到下述兩種式子:

①2*3*...*(i-1)*(i+1)*...*k*(k+1)

②3*4*...*i*(i+1)*...*k*(k+2)

顯然,我們要計算此結果,可以借助階乘,而階乘中缺失的項,我們除掉就可以了,那麽便會涉及除法取模,顯然需要用到乘法逆元

做法講解完畢,以下是為什麽連續段乘積最大的大概證明:

技術分享圖片

AC code:

技術分享圖片
 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <cstring>
 5 #define LL long long
 6 using namespace std;
 7 const int MAXN = 1e5+5;
 8 const int Mod = 1e9+7;
 9 
10 LL mul[MAXN], sum[MAXN];
11 void init()
12 {
13     mul[1] = 1;
14     sum[1] = 0;
15     for(int i = 2; i < MAXN; i++){
16         sum[i] = sum[i-1] + i;          //前綴和
17         mul[i] = (i*mul[i-1])%Mod;      //前綴積
18     }
19 }
20 
21 LL inv(LL a, int b)                     //費馬小定理求逆元
22 {
23     LL ans = 1;
24     while(b){
25         if(b&1) ans = (ans*a)%Mod;
26         a = (a*a)%Mod;
27         b >>= 1;
28     }
29     return ans;
30 }
31 
32 int main()
33 {
34     int t, x;
35     init();
36     scanf("%d", &t);
37     while(t--){
38         scanf("%d", &x);
39         if(x == 1){
40             puts("1");
41             continue;
42         }
43         int l = 2, r = MAXN, mid, p;
44         while(l <= r){
45             mid = (l+r)/2;
46             if(sum[mid] <= x) p = mid, l = mid+1;
47             else r = mid-1;
48         }
49 //        printf("%d\n", p);
50         int num = x - sum[p];
51         LL ans = 0;
52         if(num == p)
53             ans = (mul[p]*inv(2, Mod-2)%Mod*(p+2))%Mod;
54         else
55             ans = (mul[p+1]*inv(mul[p+1-num], Mod-2)%Mod*mul[p-num])%Mod;
56         printf("%lld\n", ans);
57     }
58     return 0;
59 }
60 
61 F - Detachment
View Code

2016 ACM/ICPC亞洲區大連站 F - Detachment 【維護前綴積、前綴和、二分搜索優化】