• 【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;//统计答案输出
    }
    
  • 相关阅读:
    如何查看MySQL的当前存储引擎?
    转载mysql数据库配置优化
    redis教程
    基于 Android APP 安全测试流程
    移动APP安全测试
    自动化测试用例如何进行参数化
    分享一波免费的PPT模板下载网站
    UI自动化-滑块验证码识别 java版本
    安全测试博客汇总
    妙手回春——GRUB系统引导器恢复指南
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1974.html
Copyright © 2020-2023  润新知