《怪物獵人崛起》金獅子招式派生解析
阿新 • • 發佈:2022-01-28
數位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; }