【BZOJ1974】[SDOI2010] 代码拍卖会(动态规划)

点此看题面

大致题意: 让你生成一个(n)位数,满足从高到低每一位不降,求这个数是(p)的倍数的方案数。

前言

终于明白,现在的我与过去相比,唯一进步的,就是能在错误的道路上走得更远。

很奇怪,每次做题总是搞错方向,却又偏偏能推出很多性质来,搞得非常正确一样。

虽然有了深刻自我认识之后我经常试着换一种思路,结果从未成功过。

菜是原罪,或许菜已经融化于我的血肉中,铭刻在我的骨髓里,烙印在我的灵魂上,使我永远都只能是这么菜。

转化

考虑如果我们把右起第(i)位作为一个关键点(即改变了数的大小),则就相当于是给这个数加上了(11...1)(i)(1))。当然如果这个数比前一位大了不止(1),就相当于加上多个(11...1)

于是题意就转化成了,求至多九个完全由(1)组成的数,它们的和是(p)的倍数的方案数。

归类

由于最后要求是(p)的倍数,容易发现模(p)同余的数其实对余数的影响是一样的。

换言之,我们只要统计出(tot_x)表示模(p)(x)的数的个数,就根本无需考虑我们选择的具体是什么数。

然后考虑如何求(tot_x)。显然,我们暴力枚举(1,11,111,...,11...1),当某一刻两个数模(p)同余,就意味出现了循环。而这个计算应该是比较简单的吧,可见代码。

动态规划

好,现在我们求出了(tot_x),然后就可以开心地动态规划了。

我们设(f_{i,j,k})表示处理过余数为(0sim i-1)的数(这个定义比较鬼畜,主要是为了方便刷表法),共选择了(j)个数,当前余数为(k)的方案数

则只要枚举一个(x)表示选择(x)个余数为(i)的数,就可以转移至(f_{i+1,j+x,(k+i imes x) mod p})

注意这里的转移系数,由于是在(tot_i)种数中选(x)个,我们添上(x-1)个数,其中第(a)个表示第(a+1)个数是否和第(a)个数选的是同一个数,因此就是(C_{tot_i+x-1}^{x})。显然这个东西可以在枚举(x)的同时一并维护。

还有注意初始条件,由于第(n)位是必须选的,所以我们初始化(f_{0,1,t}=1)(其中(t)表示(n)(1)(p)的余数)。

具体实现详见代码。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define P 500
#define LL long long
#define X 999911659
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
LL n;int p,s[P+5],vis[P+5],tot[P+5],f[P+5][10][P+5];
const int Inv[10]={1,1,499955830,666607773,249977915,199982332,833259716,571378091,624944787,222202591};//懒得写预处理,直接手算打表
int main()
{
	RI i,j,k,x,t;scanf("%lld%d",&n,&p),x=1%p,t=0;W(!vis[x]) s[vis[x]=++t]=x,x=(10*x+1)%p;//找循环
	for(i=0;i^p;++i) vis[i]&&vis[i]<=n&&(tot[i]=vis[i]<vis[x]?1:((n-(vis[i]-1)-1)/(t-vis[x]+1)+1)%X);//计算每个数出现次数
	f[0][1][s[vis[x]+(n-vis[x])%(t-vis[x]+1)]]=1;//初始条件
	RI C;for(i=0;i^p;++i) for(j=1;j<=9;++j) for(k=0;k^p;++k)//枚举i,j,k刷表
	{
		C=1,Inc(f[i+1][j][k],f[i][j][k]);//初始化组合数,并直接转移不选的情况
		for(x=1;x+j<=9;++x) C=1LL*C*(tot[i]+x-1)%X*Inv[x]%X,Inc(f[i+1][x+j][(k+i*x)%p],1LL*C*f[i][j][k]%X);//维护组合数,DP转移
	}
	RI ans=0;for(i=1;i<=9;++i) Inc(ans,f[p][i][0]);return printf("%d",ans),0;//统计答案输出
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1974.html