【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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

桌上那张发黄的相片,唤起我心中无尽的思念,

我终于失去你的那一天,我才明白你就是永远……——————汪峰《窗台》

         

posted @ 2017-10-13 14:05  小米狐  阅读(356)  评论(1编辑  收藏  举报
TOP