• [总结]数位统计模板


    偷看了下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;
    }

    其实和翁教主,肖神讨论后,想到了更多的鬼畜题,以后再补充吧。

  • 相关阅读:
    第一个gulp程序
    r.js打包
    吃饭途中的回忆
    IE下script标签的readyState属性
    CSS 选择器
    html的base标签
    迷你MVVM框架 avalonjs 1.3.9发布
    2014年的年终总结
    Visual Studio2017 数据库架构比较
    MVC开发中自定义返回类型
  • 原文地址:https://www.cnblogs.com/jffifa/p/2644847.html
Copyright © 2020-2023  润新知