【问题描述】
你是能看到第二题的friends呢。
——laekov
Hja和Yjq在玩游戏,这个游戏中Hja给了Yjq两个数,希望Yjq找到一些非负整数使得这些数的和等于n,并且所有数模maaaaaaaaaaaaaa 的值互不相同,求方案数。
【输入格式】
一行两个整数。
【输出格式】
一行一个整数代表答案对取模之后的结果。
【样例输入1】
3 3
【样例输出1】
9
【样例输入2】
523 44
【样例输出2】
338398304
【数据规模与约定】
对于100%的数据, n<=1e18 ,m<=100
题解:
①根据题意列出构造背包体积的式子:
a1+a2+a3……+ak=n 然后%m得到:
b1+b2+b3……+bk=n'(其中:bi=ai%m,n'=n%m)
②那么由于m的范围: 0<=bi<m 此时可以进行背包,物品体积独一无二。
③又因为∑bi 的最大值为:1+2+3+……+(m-1)=m*(m-1)/2 ,注意到可能会超过m
④令f[i][j]表示总共用了i个物品构造体积为j的方案数,就正常的背包DP转移就是了。
⑤此时的结果是取模下的结果,又因为:n=n'+k*m ,相当于物品总共还要增加k*m的权值
⑥由于状态记录了物品个数,也就是将k个m分给i个物品,此处为组合数计数(插隔板…)
⑦这道题由于要求内部组合顺序算作不同方案,因此需要乘上排列数。
⑧总结上述方法,答案为∑f[i][j]*C(插隔板)*(i!) (1<=i<=m,j=n%m+k*m且j<=m*(m-1)/2)
⑨主程序化简递推组合数式子可达O(n3),但是预处理依旧是O(n4),不要相信时间复杂度。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#ifdef unix
#define LL "%lld"
#else
#define LL "%I64d"
#endif
const int maxn=110;
const int maxs=maxn*maxn/2;
const int mo=905229641;
long long n;
int m,f[maxn][maxs],fac[maxn];
int calc(long long a,long long b)
{
long long ans=1;
for(int c=1;c<a;c++)ans=ans*(b+c)%mo;
return (int)ans;
}
int main()
{
scanf(LL "%d",&n,&m);
f[0][0]=1;
int up=m*(m-1)/2;
for (int a=0;a<m;a++)
for (int b=m;b>=0;b--)
for (int c=up;c>=0;c--)
if (f[b][c]) f[b+1][c+a]=(f[b+1][c+a]+f[b][c])%mo;
fac[0]=1;
for (int a=1;a<=m;a++)
fac[a]=(long long)fac[a-1]*a%mo;
int x=(int)(n%m);
int ans=0;
for (int a=x;a<=n && a<=up;a+=m)
{
long long rest=(n-a)/m;
for (int b=1;b<=m;b++)
if (f[b][a])
{
int nowans=calc(b,rest%mo);
nowans=(long long)nowans*f[b][a]%mo;
nowans=(long long)nowans*b%mo;
ans=(ans+nowans)%mo;
}
}
printf("%d\n",ans);return 0;
}//Ztraveler
桌上那张发黄的相片,唤起我心中无尽的思念,
我终于失去你的那一天,我才明白你就是永远……——————汪峰《窗台》