【ZBH选讲·模数和】
【问题描述】
你是能看到第二题的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
桌上那张发黄的相片,唤起我心中无尽的思念,
我终于失去你的那一天,我才明白你就是永远……——————汪峰《窗台》