Detachment HDU - 5976(数学+逆元+前缀和前缀积)

Detachment HDU - 5976

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,a2, … (x= a1+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 ( ai≠aj when i≠j).
2.Make this multidimensional space size s as large as possible (s= a1∗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)
Input
The 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^9
Output
Maximum 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为自然数),使这些数的乘积最大,输出最大乘积。

分析:

首先:有一点应该是大家都认同的,那就是不能分出1来,因为1乘任何数都是它本身,而因为分出了1,另一部分也变小了,白白使整个乘积都变小了
第二:尽量将数n分成连续的数之和能使得乘积最大即 2 , 3 , 4..... r 之积
我的思路是这样想,假设我们将数n分成两个数之和 r 1 , r 2 ,那么如果把 r 1 再次分解成非1的两个数相加 r 11 , r 12 ,那么这两个数的乘积一定大于 r 1 ,对于 r 2 也是这样。因此我们只要可以不断分解下去,那么分解后的肯定更大,而根据题意每个数不可相同,那么我们最终最多就只能分解成尽量连续的数之和的形式。

为什么说是尽量呢?因为很明显,并不是所有的数都可以分解成从2开始连续的数之和的形式,大部分的数会有一个剩余部分 Δ x
假设我们先将数n分解成了 2 + 3 + 4 + . . . + l + Δ x 的形式
我们发现 0 Δ x l
Δ x = 0 则说明恰好分成连续数之和,为什么必须小于等于l呢
因为如果 Δ x > l Δ x l + 1 ,那我们干嘛还把 l + 1 放在 Δ x 中呢,我们肯定就得到了 2 , 3 , . . . l , l + 1 了所以 0 Δ x l

根据 0 Δ x l ,我们为了让乘积最大,并根据我们之前的分析,我们不能把 Δ x 单独作为一个数和其他部分相乘,因为它的范围肯定和那些连续的数有重复

所以再根据我们一开始的分析,要尽可能分解,那么我们就把它分成1,分给那些连续的数,为了使乘积最大,我们应该把1从后往前分,因为从前往后分,很容易分完后发现出现了重复元素(即并不够分给每一个数一个1的情况,那么必将和下一个数相同)

这样我们的思路就清楚了,先找最大的小于等于n的连续数之和,然后剩下的部分从后往前一个一个分配。
这样分配后我们得到两种情况:

  1. Δ x = l ,因为我们分出的连续的数是 2 , 3 , 4... l ,一共有l-1个数,那么说明l分完一圈后还剩下1个1,我没再把这个1分给最后一个,这样就得到数列
    3 , 4 , 5.... l , l + 2

  2. 其他情况从后往前分得到数列
    2 , 3 , 4... k , k + 2... l , l + 1
    即中间有一个相差2的分解处
    当然了如果 Δ x = l 1 则每个数恰好分得一个1,是这种情况的一种特殊情况 3 , 4 , 5... l , l + 1 和这种情况按一种方法算即可

为了优化我们当然不能每次都重新算乘积,每次一个一个的尝试把它分解成最大的小于数n的连续数
因此我们可以预处理一个从2开始的前缀和sum[i],前缀积mul[i]。这样我们二分查找小于等于n的最大前缀和就得到了分解成连续数之和的那个最大数,即上面分析中的l,n-前缀和得到剩余部分 Δ x .

根据我们上面分的两种情况

  1. Δ x = l 时,先得到连续数的乘积mul[l],l二分已经得到,然后比较分配后的序列,少了2,多了l+2,这样我们除去2,乘l+2即可,注意因为涉及取模故除2要变成乘2的逆元的形式
  2. 其他情况中间肯定有个分界处,即 k , k + 2 ,那么从2到k算作一部分,从k+2到l+1算一部分。对于2~k这部分直接乘上既可以了 k = l Δ x ,对于后边部分我们可以用mul[l+1]/mul[k+1]就得到了k+2~l+1, k + 1 = l Δ x + 1

code:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
const int mod = 1e9+7;
typedef long long ll;
ll mul[maxn],sum[maxn];
void init(){
    mul[1] = 1;
    sum[1] = 0;
    for(int i = 2; i < maxn; i++){
        sum[i] = sum[i-1] + i;
        mul[i] = (i * mul[i-1]) % mod;
    }
}
ll inv(ll a,ll b){
    ll ans = 1;
    while(b){
        if(b & 1)
            ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}
int main(){
    init();
    int t;
    scanf("%d",&t);
    while(t--){
        ll x;
        scanf("%lld",&x);
        if(x <= 4){
            printf("%lld\n",x);
            continue;
        }
        int l = 2,r = maxn,mid,p;
        while(l <= r){
            mid = l + r >> 1;
            if(sum[mid] <= x) p = mid,l = mid + 1;
            else r = mid - 1;
        }
        int num = x - sum[p];
        ll ans = 0;
        if(num == p)
            ans = mul[p] * inv(2,mod-2) % mod * (p + 2) % mod;
        else
            ans = mul[p+1] * inv(mul[p-num+1],mod-2) % mod * mul[p-num] % mod;
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/codeswarrior/article/details/81149653