DZY Loves Partition
Accepts: 154
Submissions: 843
Time Limit: 4000/2000 MS (Java/Others)
Memory Limit: 262144/262144 K (Java/Others)
问题描述
DZY喜欢拆分数字。他想知道能否把n拆成恰好k个不重复的正整数之和。 思考了一会儿之后他发现这个题太简单,于是他想要最大化这k个正整数的乘积。你能帮帮他吗? 由于答案可能很大,请模109+7输出。
输入描述
第一行t,表示有t组数据。 接下来t组数据。每组数据包含一行两个正整数n,k。 (1≤t≤50,2≤n,k≤109)
输出描述
对于每个数据,如果不存在拆分方案,输出−1;否则输出最大乘积模109+7之后的值。
输入样例
4 3 4 3 2 9 3 666666 2
输出样例
-1 2 24 110888111
Hint
第一组数据没有合法拆分方案。 第二组数据方案为3=1+2,答案为1×2=2 第三组数据方案为9=2+3+4,答案为2×3×4=24。注意9=3+3+3是不合法的拆分方案,因为其中包含了重复数字。 第四组数据方案为666666=333332+333334,答案为333332×333334=111110888888。注意要对109+7取模后输出,即110888111。
思路:
首先试了一下: 9拆成3个可以拆分成2+3+4 or 1+3+5,但是很明显前一个的积要大,而且多试了几个都是如此。所以考虑拆成一段连续的数,它们积较大。但是n拆成k个连续数的和后可能有剩余,于是可以考虑吧它们添加到这k个数上面。很明显从前面添加会有重复,所以如果剩余了m,则在倒数m个数上面均加上1.
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
typedef long long ll;
#define LL(x) (x<<1)
#define RR(x) (x<<1|1)
const ll MOD = 1e9 + 7;
const int maxn = 305;
int main()
{
int T;
ll n,k;
ll t;
scanf("%d",&T);
while(T--)
{
scanf("%I64d%I64d",&n,&k);
if(k%2) t = (ll)(k+1)/2*k;
else t = (ll)k/2*(k+1);
if(t > n)
{
printf("-1\n");
continue ;
}
else
{
ll po = n-t;
ll bei = po/k;
ll els = po%k;
//cout << bei <<" "<<els<<endl;
ll ans = 1;
for(ll i = k; i >= 1; i--)
{
ll cur = i+bei;
if(els)
{
cur++;
els --;
}
ans = (ll)ans*cur%MOD;
}
printf("%I64d\n",ans);
}
}
return 0;
}