您的位置:首页 > 其它

GDOI2013 整数分拆

2016-04-04 16:32 246 查看

题目描述

题目要求求一个正整数nn的分拆成kk个数的方案。其中要求满足。

1、a1+a2+...+ak=n1、a_1+a_2+...+a_k=n

2、a1∗b1≤a22、a_1*b_1\leq a_2

3、a2∗b2≤a33、a_2*b_2\leq a_3



k、ak−1∗bk−1≤akk、a_{k-1}*b_{k-1}\leq a_k

其中bb会读入。

n≤105n\leq 10 ^5,k≤10k \leq 10,bi≤1000b_i \leq 1000.

解题思路

我们可以把原式改写:

a1=a1a_1=a_1

a2=a1∗b1+x1a_2=a_1*b_1+x_1

a3=a2∗b2+x2=a1∗b1∗b2+x1∗b2+x2a_3=a_2*b_2+x_2=a_1*b_1*b_2+x_1*b_2+x_2



a1+a2+...+ak=na_1+a_2+...+a_k=n.

这样我把前面的kk个等式带入最后的等式,就得到了一个只含有a1a_1和xix_i的方程,其中a1≥1a_1 \geq 1.求方程的解的个数就行了。

设fi,jf_{i,j}表示前ii个未知数前ii项的和为jj的方案数。这样可以O(1)O(1)转移,总时间O(nk)O(nk).

参考代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define maxn 100005
#define maxk 15
#define mo 1000000007
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;

int b[maxk],s[maxk];

int T,n,k;

int f[maxk][maxn];

ll x[maxk];

int main(){
scanf("%d",&T);
while (T--) {
scanf("%d%d",&n,&k);
fo(i,1,k-1) scanf("%d",&b[i]);
s[0]=1;
bool ok=0;
fo(i,1,k-1) {
s[i]=s[i-1]*b[i];
if (s[i]>n) {
ok=1;
break;
}
}
if (ok) {
puts("0");
continue;
}
mem(f,0);
fo(i,0,k-1) {
x[i]=1;
fo(j,i+1,k-1) {
x[i]+=s[j]/s[i];
if (x[i]>n) {
x[i]=n+1;
break;
}
}
}
fo(i,1,n / x[0])
f[0][i*x[0]]=1;
fo(i,1,k-1) {
fo(j,0,n) f[i][j]=f[i-1][j];
fo(j,x[i],n) f[i][j]=(f[i][j]+f[i][j-x[i]]) % mo;
}
printf("%d\n",f[k-1]
);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: