P2481 SDOI2010代码拍卖会



$ solution: $

这道题调了好久好久,久到都要放弃了。洛谷的第五个点是真的强,简简单单一个1,调了快4个小时!

这道题第一眼怎么都是数位DP,奈何数据范围太大,只能找性质。而这道题最重要的一个性质也很套路(敲难想),因为我们所有的方案都是 $ 111112223333 $ 这样的数,我们要求的是它的大小模 $ p $ ,所以我们考虑将它用加法拆解。于是我们惊奇的发现它可以转化成(以上面那个数为例) $ 111111111111+1111111+1111=111112223333 $ 。正因为数位上的数字单调不降,所以它可以表示为不超过9个形似 $ 111...111 $ 的数的和!

而这种形似 $ 1111...1111 $ 的数,我们可以发现,如果我们依次枚举它的长度( $ 1,11,111,1111...... $ )这些数在模 $ p $ 意义下一定会产生循环节。于是我们将在模 $ p $ 意义下同余的这类数归到一起,然后用一个数组 $ g[i] $ 记录模 $ p $ 意义下为 $ i $ 的形如 $ 11111...111 $ 的数的个数。(因为 $ p $ 不大,所以不用枚举多久就能找到循环节)

于是题目的意思就被转换成:从所有的 $ g[i] $ 中找出(不超过)九个数,使得它们的下标之和能被 $ p $ 整除!(为什么是下标之和?因为上面说了 $ g[i] $ 中所有的形如 $ 1111...111 $ 的数在模 $ p $ 意义下为 $ i $ !)

所以这道题就变成了一道计数类DP,我们设 $ f[i][j][o] $ 表示:已经选完了下标为 $ [0,i-1] $ 的数,从中选了 $ o $ 个形如 $ 1111...11 $ 的数,它们的和模 $ p $ 等于 $ j $ 的方案数。 考虑它的转移,如果我们要从下标为 $ i $ 的数里选出 $ k $ 个数,显然这 $ k $ 个数是可以重复的,他们的贡献都为 $ i $ ,所以相当我们从 $ g[i] $ 个数里可重复的选出 $ k $ 个数(不论怎么选他们的和都为 $ k*i $ ),根据组合计数原理这样的方案有 $ C_{g[i]+k-1}^{~~~~~k} $ 种。为了方便博主代码里会用 $ C[i][k] $ 代替 $ C[g[i]+k-1][k] $ 。

$ f[i+1][(j+k*i)modp][o+k]=f[i][j][o]*C[i][k] $



$ code: $

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set> #define ll long long
#define db double
#define rg register int using namespace std; const int mod=999911659; //答案的模数 ll n;
ll g[505]; //记录余数为i的形如11111的数的个数
int p,vn,ans; //循环节长度,小模数
int a[505]; //记录余数上一次的位置(找循环节用)
int inv[11]; //逆元
int c[505][11]; //组合数
int f[505][505][11]; //计数类DP inline int qr(){
register char ch; register bool sign=0; rg res=0;
while(!isdigit(ch=getchar()))if(ch=='-')sign=1;
while(isdigit(ch))res=res*10+(ch^48),ch=getchar();
if(sign)return -res; else return res;
} int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
scanf("%lld%d",&n,&p); inv[1]=1;
if(n<=p) for(rg i=1;i<=n;++i) vn=(vn*10+1)%p,++g[vn];
else{ rg tot=1%p,m,l;
for(rg i=1;i<=p+1;++i){
if(a[tot]){ l=a[tot]; m=i-l; break;} //找到循环节,计算循环节长度
a[tot]=i; ++g[tot]; tot=(tot*10+1)%p; //记录当前余数出现位置,继续寻找
}
for(rg i=0;i<p;++i)
if(a[i]&&a[i]>=l){
g[i]+=(n-a[i])/m%mod; //记录所有余数为i的形如11111的数的个数
if((a[i]-l+1)%m==(n-l+1)%m) vn=i; //记录长度为 n 的形如1111的数的余数,后面初始DP时有用
}
}
for(rg i=2;i<9;++i) inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod; //线性递推求逆元
for(rg i=0;i<p;++i){ c[i][0]=1; if(!g[i])continue;
for(rg j=1;j<9;++j,g[i]=(g[i]+1)%mod) //注意最后还有个++g[i]!!!
c[i][j]=(ll)c[i][j-1]*g[i]%mod*inv[j]%mod; //用性质求i较大,j较小的逆元
}
f[0][vn][0]=1; //最开始有一个长度为n的形如1111111111的数
for(rg i=0;i<p;++i)
for(rg j=0;j<p;++j)
for(rg o=0;o<9;++o)
for(rg k=0;k<9-o;++k) //枚举取出多少个余数为i的形如的11111111的数
(f[i+1][(j+k*i)%p][k+o]+=(ll)f[i][j][o]*c[i][k]%mod)%=mod;
for(rg i=0;i<9;++i) ans=(ans+f[p][0][i])%mod; //计算答案
printf("%d\n",ans);
return 0;
}

SDOI2010代码拍卖会 (计数类DP)的更多相关文章

  1. 动态规划——区间DP,计数类DP,数位统计DP

    本博客部分内容参考:<算法竞赛进阶指南> 一.区间DP 划重点: 以前所学过的线性DP一般从初始状态开始,沿着阶段的扩张向某个方向递推,直至计算出目标状态. 区间DP也属于线性DP的一种, ...

  2. CH5E26 扑克牌 (计数类DP)

    $ CH~5E26~\times ~ $ 扑克牌: (计数类DP) $ solution: $ 唉,计数类DP总是这么有套路,就是想不到. 这道题我们首先可以发现牌的花色没有价值,只需要知道每种牌有 ...

  3. $Poj1737\ Connected\ Graph$ 计数类$DP$

    AcWing Description 求$N$个节点的无向连通图有多少个,节点有标号,编号为$1~N$. $1<=N<=50$ Sol 在计数类$DP$中,通常要把一个问题划分成若干个子问 ...

  4. $CF559C\ Gerald\ and\ Fiant\ Chess$ 计数类$DP$

    AcWing Description 有个$H$行$W$列的棋盘,里面有$N$个黑色格子,求一个棋子由左上方格子走到右下方格子且不经过黑色格子的方案数. $1<=H,M<=1e5,1< ...

  5. BZOJ 1974 [Sdoi2010] auction 代码拍卖会(数位dp)

    题目描述 随着iPig在P++语言上的造诣日益提升,他形成了自己一套完整的代码库.猪王国想参加POI的童鞋们都争先恐后问iPig索要代码库.iPig不想把代码库给所有想要的小猪,只想给其中的一部分既关 ...

  6. [SDOI2010]代码拍卖会——DP

    原题戳这里 绝对是一道好题 需要注意到两个东西 1.符合条件的数可以拆成一堆\(11...11\)相加的形式,比如\(1145=1111+11+11+11+1\) 2.\(1,11,111,1111, ...

  7. Luogu2481 SDOI2010 代码拍卖会 DP、组合

    传送门 神仙DP 注意到\(N \leq 10^{18}\),不能够直接数位DP,于是考虑形成的\(N\)位数的性质. 因为低位一定不会比高位小,所以所有满足条件的\(N\)位数一定是不超过\(9\) ...

  8. [SDOI2010]代码拍卖会

    题目描述 随着iPig在P++语言上的造诣日益提升,他形成了自己一套完整的代码库.猪王国想参加POI的童鞋们都争先恐后问iPig索要代码库.iPig不想把代码库给所有想要的小猪,只想给其中的一部分既关 ...

  9. bzoj 1974: [Sdoi2010]代码拍卖会

    Description 随着iPig在P++语言上的造诣日益提升,他形成了自己一套完整的代 码库.猪王国想参加POI的童鞋们都争先恐后问iPig索要代码库.iPi g不想把代码库给所有想要的小猪,只想 ...

随机推荐

  1. 506C Mr. Kitayuta vs. Bamboos

    分析 代码 #include<bits/stdc++.h> using namespace std; #define int long long ],h[],now[],cnt[]; in ...

  2. Win7上防火墙开放FTP服务以及ping解决方案

    1.windows 防火墙开放ftp服务 The following 4 steps will allow both non-secure and SSL FTP traffic through fi ...

  3. Jmeter之仅一次控制器

    在进行压测时,往往登录只需要一次,其他的接口需要循环测试.这里就会用到控制器中的仅一次控制器.只要放置在控制器的请求,不管在哪种情况下,都只执行一次. 1.名称:可以标识在该控制器下主要完成的 工作内 ...

  4. 【奇技淫巧】linux 定时任务 crontab 反弹 shell

    日期:2018-11-26 13:47:34 介绍:如何使用定时任务来反弹 shell? 0x01. 基本命令 参数 -e:编辑该用户的计时器设置: -l:列出该用户的计时器设置: -r:删除该用户的 ...

  5. 2018.03.26 Python-Pandas 字符串常用方法

    import numpy as np import pandas as pd 1 #字符串常用方法 - strip s = pd.Series([' jack ','jill',' jease ',' ...

  6. ES6中数组和对象的扩展运算符拷贝问题以及常用的深浅拷贝方法

    在ES6中新增了扩展运算符可以对数组和对象进行操作.有时候会遇到数组和对象的拷贝,可能会用到扩展运算符.那么这个扩展运算符到底是深拷贝还是浅拷贝呢? 一..使用扩展运算符拷贝 首先是下面的代码. le ...

  7. 远程桌面 使用 本地输入法(虚拟化 终端 远程接入 RemoteApp)

    远程桌面连接组件是微软从Windows 2000 Server开始提供的,该组件一经推出便受到了很多用户的拥护和使用.   在WINDOWS XP和WINDOWS SERVER 2003中微软公司将该 ...

  8. webservice的一些理解

    web services中如果用.NET的话,DataSet可以作为与客户端交互的一个返回值,因为DataSet实质上是XML.而SOAP WSDL等都是基于XML的. --------------- ...

  9. 浅拷贝&深拷贝

    浅拷贝新的对象指向原来对象的地址 深拷贝新的对象中,原来是可变对象,会新复制一份值指向新的地址[11,22,33]若原来的对象里含有可变对象,里面的这个可变对象也会指向新的地址['qwer', 123 ...

  10. Mysql函数----控制流函数介绍

    MySQL有4个函数用来进行条件操作的,可以实现SQL的条件逻辑,允许开发者将一些应用程序业务逻辑转换到数据库后台.   MySQL控制流函数: 1.CASE WHEN[test1] THEN [re ...