数位dp
数位dp
P2657 [SCOI2009] windy 数
题目大意:
不含前导零且相邻两个数字之差至少为 \(2\) 的正整数被称为 windy 数。windy 想知道,在 \(a\) 和 \(b\) 之间,包括 \(a\) 和 \(b\) ,总共有多少个 windy 数?
\(a,b \le 2\times 10^9\)
思路:
\(f[i][j]\)表示一个\(i\)位数,最高位为\(j\),包括的windy数。例如\(f[3][6]\)统计的是\(600~699\)的答案。
统计时依次统计,要注意一些细节。
核心代码:
void init(){
for(long long i=0;i<=9;i++)f[1][i]=1;
for(long long i=2;i<=10;i++){
for(long long j=0;j<=9;j++){
for(long long w=0;w<=9;w++){
if(abs(j-w)<2)continue;
f[i][j]+=f[i-1][w];
}
}
}
}
long long dp(long long x){
if(!x)return 0;
long long res=0,ws[15]={0},tot=0,xx=x;
while(xx>0){
ws[++tot]=xx%10;
xx/=10;
}
bool ok=1;
ws[tot+1]=999;
for(long long i=tot;i>=1;i--){
for(long long j=1;j<=9;j++)res+=f[i-1][j]; // 如果第i位是前导0,记录i-1位的答案
if(ok)
for(long long j=0;j<ws[i];j++)
if((i!=tot||j!=0)&&abs(ws[i+1]-j)>=2)
res+=f[i][j];
if(abs(ws[i]-ws[i+1])<=1)ok=0; // 此处不能break,例如123,需要统计0-9,10-99的答案
}
long long last=999;
ok=1;
while(x>0){
if(abs(x%10-last)<=1){
ok=0; break;
}
last=x%10; x/=10;
}
res+=ok;
return res;
}
P2602 [ZJOI2010]数字计数
题目大意:
给定两个正整数 \(a\) 和 bbb,求在 \(\[a,b\]\) 中的所有整数中,每个数码(digit)各出现了多少次。
\(a,b \le 10^12\)
思路:
直接统计即可,要明白\(f[i][j]\)的意义,\(f[3][6]\)表示\(6000-6999\),不是\(1-6999\)。
核心代码:
void init(){
for(LL i=0;i<=9;i++)f[1][i].c[i]=1;
for(LL i=2;i<=12;i++){
for(LL j=0;j<=9;j++){
for(LL w=0;w<=9;w++)
f[i][0].c[w]+=f[i-1][j].c[w];
}
for(LL j=1;j<=9;j++){
f[i][j].c[0]=f[i][0].c[0];
for(LL w=1;w<=9;w++)
f[i][j].c[w]=f[i][0].c[w]+(LL)(j==w)*(t10[i-1]);
}
f[i][0].c[0]+=t10[i-1];
}
}
qwe dp(LL x){
// special
if(x==0){
qwe c;
for(LL i=0;i<=9;i++)c.c[i]=0;
c.c[0]=1;
return c;
}
// translate x
LL ws[15],tot=0,xx=x;
while(x>0){
ws[++tot]=x%10;
x/=10;
}
qwe res;
//clear
for(LL i=0;i<=9;i++)res.c[i]=0;
for(LL i=tot;i>=1;i--){
if(ws[i]>=1){
for(int j=0;j<ws[i];j++){
res=res+f[i][j];
if(i==tot&&j==0){
for(int w=i-1;w>=1;w--)
res.c[0]-=t10[w];
}
}
}
res.c[ws[i]]+=(xx%t10[i-1]+1); // 别忘了给这一位自己统计上
}
return res;
}

浙公网安备 33010602011771号