http://acm.hdu.edu.cn/showproblem.php?pid=5976
题意:
前言:一维 即一条线段的长度,二维 即x*y,三维 即x*y*z, 四维 即x*y*z*h, 五维。。。n维
然后题目给你一个数x 把这个数拆成x= ai + aj +...+ak (两两不相等)
然后输出这些数累乘的结果 再模一个数 要求结果最大
结论1:贪心策略 --》要想乘积最大:连续乘最好 2*3*4*5.。。。
我们要乘积最大,那么我们把n尽可能的拆分,如果题目不要求ai≠aj,那么我们将x拆分成什么最好呢?
显然拆成2和3(2为偶数,3为奇数,2x+3y可以表示大于1的所有正整数)是最好的,顺便提下3最多的时候最优,
为什么不是2,将,6拆成3个2乘积为8,而将6拆成3乘积为9,(不会证)
注意:x最大10的九次方 测试发现最好那个数是45000 累乘结果会爆 所以只能保存取模结果
由于后面的结论推出 要除一些数 所以用到逆元
公式: inv[i] = (mod - mod / i) * inv[mod % i] % mod; //公式 递推求
或者快速幂
二分查找:找到第一个大于等于 利用负数来看比较好 lower_bound
如果找到小于等于 那个位置 接下来要补数 反而不好理解
收获:手动二分 如果那个数不存在的话 会找小于等于这个数的位置 如果结果加1 也做不到 lower那样大于等于 因为等于的情况就错了。所以以后尽量别手动,手动的话也最好是找一个确定存在的数。这样才不易错
#include<iostream>
#include<cstring>
#include<cmath>
#include<string>
#include<map>
#include<vector>
#include<set>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<functional>
using namespace std;
#define inf 0x3f3f3f3f
#define pi acos(-1.0)
#define LL long long
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
//#define mp make_pair
#define pb push_back
#define ULL unsigned LL
#define mem(a, b) memset(a, b, sizeof(a))
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
//#define fopen freopen("in.txt", "r", stdin);freopen("out.txt", "w", stout);
const LL mod = 1e9 + 7;
const int maxn = 45000;
int sum[maxn];
LL mul[maxn], inv[maxn];
int qpow(int a, int b) {
int res = 1;
LL tmp = a;//注意tmp会爆 涉及乘法
while (b) {
if (b & 1)
res = res * tmp % mod;
tmp = tmp * tmp % mod;
b >>= 1;
}
return res;
}
void init() {
sum[1] = 0, mul[1] = inv[1] = 1;
for (int i = 2; i < maxn; ++i) {
sum[i] = sum[i - 1] + i;
mul[i] = mul[i - 1] * i % mod;//由于直接保存累乘结果会爆 所以保存取模结果
//inv[i] = (mod - mod / i) * inv[mod % i] % mod;//公式 递推求
inv[i] = qpow(i, mod - 2);
}
//cout << inv[2];
}
int main() {
/*int sum = 0;
for (int i = 1; i <= 45000; ++i) {
sum += i;
}
printf("%d", sum);*/
int t;
scanf("%d", &t);
init();
while (t--) {
int x;
scanf("%d", &x);
if (x <= 4) {
printf("%d\n", x);
continue;
}
//找那么一个序列 2 + 3 + 4 + 。。。+ (最大数P - 一个负数a)
int p = lower_bound(sum + 2, sum + maxn, x) - sum;
//cout << p << endl;
//int left = 2, right = maxn, mid;//【left, right) 左闭右开
//mid = (left + right) >> 1;
//while (left + 1 < right) {//left + 1 == right 结束
// if (sum[mid] > x)
// right = mid;
// else
// left = mid;
// mid = (left + right) >> 1;
//}
//cout << mid << endl;//手动找的是小于(数不存在的话)等于x的位置
//现在求负数 因为加多了嘛 题目让我们求 这些数的积最大 同时这些数加起来要等于 x 现在加多了 要减走 即在累乘那里 除走
int a = x - sum[p];
//cout << a << endl;
//可以发现 这个a的范围 -k, -(k - 1), - (k - 2), 。。。-2, -1, 0
//这个k就是(p - 1)
/*结论:要想乘积最大:连续乘最好 2*3*4*5.。。。
我们要乘积最大,那么我们把n尽可能的拆分,如果题目不要求ai≠aj,那么我们将x拆分成什么最好呢?
显然拆成2和3(2为偶数,3为奇数,2x+3y可以表示大于1的所有正整数)是最好的,顺便提下3最多的时候最优,
为什么不是2,将,6拆成3个2乘积为8,而将6拆成3乘积为9,(不会证)*/
//现在看下怎么除去(时刻想着两个数相差越小 越优): a为0 不用去
//a为-1 把2去掉 此时又会多了一个1 加到p上 p变成p+1 即3*4*(5+1)
//剩下的情况 都有一个对应的正数 去掉对应的正数 比如a=-5 2*3*4*5
//如果去掉2和3 乘积是20 去掉5的话是24 不太理解。。。这样就是最优
//现在有mul【】 要除去相应的数 再模----》逆元
LL ans;
//可以发现比如 2*3*4*5=120 当x=13 即a==-1 根据策略 3*4*6=72
if (a == -1)
ans = mul[p] * inv[2] % mod * inv[p] % mod * (p + 1) % mod;
else if (a == 0)
ans = mul[p];
else
ans = mul[p] * inv[-a] % mod;
printf("%lld\n", ans);
}
return 0;
}