数位DP
感觉数位 DP 一般都能一眼看出来,而且代码长得也差不多诶!
dfs 式数位 DP 模板
ll dfs(ll len,bool Limit,bool zero,ll …… ) // 其他各种条件
{
if(len>w) return zero^1; // 注意!!!特判 0
if(!Limit && dp[len][……]!=-1) return dp[len][……];
ll ret=0;
for(ll i=0;i<=9;i++)
{
if(Limit && i>num[len]) break; // 只有在有 Limit 最高位限制是才需要判断是否高于最高位
if(……) // 根据题目满足各种条件
}
if(!Limit && !zero) dp[len][……]=ret; // 必须同时满足没有最高位与前导 0 限制
return ret;
}
ll solve(ll x)
{
memset(dp,-1,sizeof(dp)); // 有时要每一次都赋值!!!
ll w=0,tmp=x;
while(tmp) num[++w]=tmp%10,tmp/=10; // 统计位数
// 有的情况下应在这里给 dp 数组赋值
return dfs(w,1,1,……);
}
memset(dp,-1,sizeof(dp)); // 有时只用赋值一次!!!
例题
几个模板题:P2657 [SCOI2009] windy 数、P3413 SAC#1 - 萌数、P4127 [AHOI2009]同类分布、CF914C Travelling Salesman and Special Numbers、P2602 [ZJOI2010]数字计数。
P4317 花神的数论题
统计 \(n\) 以内正整数的二进制中 \(1\) 的个数之积。
枚举 \(k=1\dots \log_2^n\) ,分别计算二进制中 \(1\) 的个数为 \(k\) 时的数的个数,最后相乘即可。
CF288E Polo the Penguin and Lucky Numbers
一道还比较难写的数位 DP。
数位 DP 没话说,我们按照每一位向下,考虑将贡献分开来计算。
假设两个数为 \(4\times 10^p+x\) 和 \(4\times 10^p+x+1\)(将首位 \(47\) 交错的情况单独考虑),可以先计算首位的贡献,再算其他的贡献。
那么两数之积为:\(16\times 10^{2p}+4\times 10^p\times (x+x+1)+x(x+1)\)。
- 第一项可以计算出项数,直接算出;
- 第二项算出所有少去这一位的数的和的两倍减去两端,乘上首位。
- 最后一项递归为子问题。
那么直接套上数位 DP 就完成啦?我们需要记录的内容有:数的个数、数的和、答案。