[總結]數位統計模板


偷看了下7k+大牛的數位統計dp寫法,通常的數位dp可以寫成如下形式:

int dfs(int i, int s, bool e) {
    if (i==-1) return s==target_s;
    if (!e && ~f[i][s]) return f[i][s];
    int res = 0;
    int u = e?num[i]:9;
    for (int d = first?1:0; d <= u; ++d)
        res += dfs(i-1, new_s(s, d), e&&d==u);
    return e?res:f[i][s]=res;
}

其中:

f為記憶化數組;

i為當前處理串的第i位(權重表示法,也即后面剩下i+1位待填數);

s為之前數字的狀態(如果要求后面的數滿足什么狀態,也可以再記一個目標狀態t之類,for的時候枚舉下t);

e表示之前的數是否是上界的前綴(即后面的數能否任意填)。

for循環枚舉數字時,要注意是否能枚舉0,以及0對於狀態的影響,有的題目前導0和中間的0是等價的,但有的不是,對於后者可以在dfs時再加一個狀態變量z,表示前面是否全部是前導0,也可以看是否是首位,然后外面統計時候枚舉一下位數。It depends.

於是關鍵就在怎么設計狀態。當然做多了之后狀態一眼就可以瞄出來。

注意:

不滿足區間減法性質的話(如hdu 4376),不能用solve(r)-solve(l-1),狀態設計會更加詭異。

下面給幾個例子:

codeforces 55d / spoj JZPEXT(7k+的鬼畜題,卡時限以及代碼長度,所以狀態設計要注意)

//JZPEXT 
 #include<cstdio> 
 #include<cstring> 
 typedef long long ll; 
 int c[2555],n[20],g[2555][10],h[255][10]; 
 ll f[20][50][255],l,r; 
   
 int gcd(int x,int y){ 
     return y?gcd(y,x%y):x; 
 } 
   
 ll dfs(int l,int m,int r,bool z){ 
     if(l==-1)return !(r%m); 
     if(!z&&f[l][c[m]][r]!=-1) return f[l][c[m]][r]; 
     ll res=0; 
     int u=z?n[l]:9; 
     for(int d=0;d<=u;++d) 
         res+=dfs(l-1,g[m][d],l?h[r][d]:r*10+d,z&&d==u); 
     return z?res:f[l][c[m]][r]=res; 
 } 
   
 ll s(ll x){ 
     int l=0; 
     for (;x;x/=10)n[l++]=x%10; 
     return dfs(l-1,1,0,1); 
 } 
   
 int main(){ 
     memset(f,-1,sizeof f); 
     int i,j,t; 
     for(i=1,r=-1;i<=2520;++i) 
         c[i]=r+=!(2520%i); 
     for(j=0;j<10;++j){ 
         for(i=1;i<=2520;++i) 
             g[i][j]=j?i*j/gcd(i,j):i; 
         for(i=0;i<252;++i) 
             h[i][j]=(i*10+j)%252; 
     } 
     scanf("%d",&t); 
     while(t--){ 
         scanf("%lld%lld",&l,&r); 
         printf("%lld\n",s(r)-s(l-1)); 
     } 
     return 0; 
 }

hdu 4352

#include <cstdio> 
 #include <cstring> 
 #include <algorithm> 
 using namespace std; 
   
 typedef long long ll; 
   
 ll f[20][1<<10][11]; 
 int num[20]; 
 int news[10][1<<10]; 
 ll l, r; 
 int k; 
   
 int getnews(int x, int s) { 
     for (int i = x; i < 10; ++i) 
         if (s&(1<<i)) return s^(1<<i)|(1<<x); 
     return s|(1<<x); 
 } 
   
 ll dfs(int i, int s, bool e, bool z) { 
     if (i==-1) return __builtin_popcount(s)==k; 
     if (!e && f[i][s][k]!=-1) return f[i][s][k]; 
     ll res = 0; 
     int u = e?num[i]:9; 
     for (int d = 0; d <= u; ++d) 
         res += dfs(i-1, z&&!d?0:news[d][s], e&&d==u, z&&!d); 
     return e?res:f[i][s][k]=res; 
 } 
   
 ll solve(ll x) { 
     int len =0; 
     for (; x; x/=10) 
         num[len++] = x%10; 
     return dfs(len-1, 0, 1, 1); 
 } 
   
 int T; 
   
 int main() { 
     int i, j; 
     for (i = 0; i < 10; ++i) 
         for (j = 0; j < 1<<10; ++j) 
             news[i][j] = getnews(i, j); 
     memset(f, -1, sizeof(f)); 
     scanf("%d", &T); 
     for (int t = 1; t <= T; ++t) { 
         scanf("%I64d%I64d%d", &l, &r, &k); 
         printf("Case #%d: ", t); 
         printf("%I64d\n", solve(r)-solve(l-1)); 
     } 
     return 0; 
 }

spoj BALNUM

//BALNUM
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;

int num[22];
ll f[22][60000];
int T;
int news[60000][10];
int exp[10];
bool ok[60000];
ll l, r;

ll dfs(int i, int s, bool e) {
    if (i==-1) return ok[s];
    if (!e && ~f[i][s]) return f[i][s];
    ll res = 0;
    int u = e?num[i]:9;
    for (int d = s?0:1; d <= u; ++d)
        res += dfs(i-1, news[s][d], e&&d==u);
    return e?res:f[i][s]=res;
}

int getnum(ll x, int num[]) {
    int len = 0;
    for (; x; x/=10)
        num[len++] = x%10;
    return len;
}

ll solve(ll x) {
    int len = getnum(x, num);
    ll res = 0;
    for (int i = 1; i < len; ++i)
        res += dfs(i-1, 0, 0);
    return res+=dfs(len-1, 0, 1);
}

bool judge(int exp[]) {
    for (int i = 0; i < 10; ++i) {
        if (!exp[i]) continue;
        if (i&1) {
            if (exp[i]&1) return 0;
        }
        else {
            if (~exp[i]&1) return 0;
        }
    }
    return 1;
}

int main() {
    int i, j, k;
    memset(f, -1, sizeof f);
    for (int s = 0; s < 59049; ++s) {
        j = s;
        for (i = 0; i < 10; ++i, j/=3)
            exp[i] = j%3;
        if (s) ok[s] = judge(exp);
        else ok[s] = 0;
        for (i = 0; i < 10; ++i) {
            int t = exp[i];
            if (exp[i]&1) exp[i] = 2;
            else exp[i] = 1;
            for (j = 9; j >= 0; --j, news[s][i]*=3)
                news[s][i] += exp[j];
            news[s][i] /= 3;
            exp[i] = t;
        }
    }
    //printf("#%d\n", news[0][3]);
    scanf("%d", &T);
    while (T--) {
        scanf("%lld%lld", &l, &r);
        printf("%lld\n", solve(r)-solve(l-1));
    }
    return 0;
}

其實和翁教主,肖神討論后,想到了更多的鬼畜題,以后再補充吧。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM