DP(8)--数位DP

/*  https://www.luogu.com.cn/problem/P2602 */

#include
using namespace std;
// arr[i][j]: 小于等于(i+1)位数的全排列中j的个数
long long arr[12][10];
// pre[i]: 小于等于(i+1)位数中的前导0个数
long long pre[12];

long long pow(int n)
{
    long long res = 1;
    for (int i = 0; i < n; ++i)
        res *= 10;

    return res;
}

void calc(long long num, long long (&ans)[10])
{
    int i = 0, j, k, bits = 0, data[12];
    long long t = num;
    while (t)
    {
        ++bits;
        data[i++] = t % 10; 
        t /= 10;
    }

    for (j = 0; j < 10 && bits > 1; ++j)  //[1, 10^(bits-1))
        ans[j] += arr[bits-2][j];
    t = pow(bits-1);
    for (j = 1; j < data[bits-1]; ++j)
        ans[j] += t;

    if (data[bits-1] > 1) // [10^(bits-1), data[bits-1]*10^(bits-1) - 1)
        for (j = 0; j < 10; ++j)
            ans[j] += arr[bits-2][1] * (data[bits-1] - 1);

    for (i = 0; i < (bits-1); ++i)
    {
        t = pow(i);
        if (i < 2)
        {
            if (i == 0) // 处理最低位
            {
                for (j = 0; j <= data[i]; ++j)
                    ++ans[j];
                for (k = i+1; k < bits; ++k)
                    if (data[i] == 0)
                        ++ans[data[k]];
                    else
                        ans[data[k]] += (data[i]+1);
            }
            else
            {
                for (j = 0; j < 10; ++j)
                    ans[j] += data[i];
                for (j = 0; j < data[i]; ++j)
                    ans[j] += t;
                for (k = i+1; k < bits; ++k)
                    ans[data[k]] +=  t * data[i];
            }
        }
        else
        {
            if (data[i] > 0)
            {
                for (k = 0; k < 10; ++k)
                    ans[k] += t * i / 10 * data[i];
                for (j = 0; j < data[i]; ++j)
                    ans[j] += t;
                for (k = i+1; k < bits; ++k)
                    ans[data[k]] +=  t * data[i];
            }
        }
    }
}


int main()
{
    long long a, b, t;
    cin >> a >> b;

    int i, j;
    for (i = 0; i < 10; ++i)
        arr[0][i] = 1;

    pre[0] = 1;
    for (i = 1; i < 12; ++i)
    {
        t = pow(i);
        for (j = 0; j < 10; ++j)
            arr[i][j] = t + arr[i-1][j] * 10;

        pre[i] = t + pre[i - 1];
    }

    for (i = 1; i < 12; ++i)
        arr[i][0] -= pre[i];

    long long ans[10] = { 0 };
    long long res[10] = { 0 };
    if (a > 1)
        calc(a-1, res);
    else
        res[0] = 1;

    calc(b, ans);
    
    for (j = 0; j < 10; ++j)
        cout << ans[j] - res[j] << " ";
    cout << endl;

    return 0;
}

/* https://www.luogu.com.cn/problem/P2657 


f[i][j]: 具有i位,最高位数字为j,满足windy数定义的数的个数。

状态转移: 第i位的值为j,设第i-1位的值为k,
根据windy数定义只要满足abs(k-j) >= 2就可以进行状态转移
状态转移方程:
 f[i][j] = if (abs(k-j) >= 2) { ∑f[i-1][k], (k∈[0,9] }

*/

#include
#include
using namespace std;

int f[11][10];

int dp(int n)
{
    int bits = 0, res = 0;
    int arr[11];

    while (n)
    {
        arr[bits++] = n%10;
        n /= 10;
    }

    int i, j, pre = -2;

    // 处理 bits位数[1x...x, n]
    for (i = bits-1; i >= 0; --i)
    {
        for (j = !!(i == bits-1); j < arr[i]; ++j)
            if (abs(j - pre) >= 2)
                res += f[i+1][j];

        if (abs(arr[i] - pre) < 2)
            break;

        pre = arr[i];

        if (i == 0) // n 是 windy 数
            ++res;
    }
    // 处理带有前导0的数,即小于bits位的数[1, 0x...x], bits-1位x
    for (i = 1; i < bits; ++i)
        for (j = 1; j <= 9; ++j)
            res += f[i][j];

    return res;
}

int main()
{
    int a, b, i, j, k;
    cin >> a >> b;

    for (i = 0; i <= 9; ++i)
        f[1][i] = 1;

    for (i = 2; i < 11; ++i)
        for (j = 0; j <= 9; ++j)
            for (k = 0; k <= 9; ++k)
                if (abs(j - k) >= 2)
                    f[i][j] += f[i-1][k];


    int ans = dp(b);
    if (a > 1)
        ans -= dp(a-1);

    cout << ans << endl;

    return 0;
}

//https://www.luogu.com.cn/problem/P3311

#include
#include
#include
using namespace std;

const int MOD = 1000000007;
const int N = 1501;
//当前从高到低已经填了i位(即在AC自动机上走过了i条边),此时停在标号为j的节点上,当前是否正好贴着上界
//dp[i][j][0]: 当前位置(i)的值不能超过(小于)所给整数的对应位(s[i])
//dp[i][j][1]: 当前位置的值等于所给整数的对应位(之前已经有一位小于所给整数的对应位置)
long long dp[N][N][2]; 
int tot;
int tr[N][10]; // tr[p]['c']=u: p 通过字符'c'指向u;还被用于更新转移边
bool e[N];      // e[u]: 节点u是结尾
int fail[N];

void insert(char *s)
{
    int u = 0;
    for (int i = 0; s[i] != '\0'; ++i)
    {
        if (tr[u][s[i]-'0'] == 0) // 不在Trie中,插入当前结点
            tr[u][s[i]-'0'] = ++tot;
        u = tr[u][s[i]-'0'];
    }
    e[u] = true;
}

void build()
{
    queue qu;
    // 0: root node
    for (int i = 0; i < 10; ++i)
        if (tr[0][i])
            qu.push(tr[0][i]);

    while (qu.size() > 0)
    {
        int u = qu.front();
        qu.pop();
        for (int i = 0; i < 10; ++i)
        {
            int v = tr[u][i];
            if (v)
            {
                fail[v] = tr[fail[u]][i];
                qu.push(v);
                e[v] |= e[fail[v]];

                //cout << "fail[" << v << "]: " << fail[v] << endl;
            }
            else //将不存在的字典树的状态链接到了失配指针的对应状态
                tr[u][i] = tr[fail[u]][i];

            //cout << "tr[" << u << "][" << char(i+'0') << "]: " << tr[u][i] << endl;
        }
    }

    tr[0][0] = 0; // 特殊处理包含前导0的模式串
}

inline void add(long long &x, long long y) { x = (x + y) % MOD; }


int main()
{
    char s[N];
    char str[N];
    cin >> s;
    int i, j, k, m, len = strlen(s);
    cin >> m;
    for (i = 0; i < m; ++i)
    {
        cin >> str;
        insert(str);
    }
    build();

    dp[0][0][1] = 1;
    for (i = 0; i < len; ++i)
        for (j = 0; j <= tot; ++j)
            if (!e[j])
            {
                for (k = 0; k < 10; ++k)
                {
                    if (!e[tr[j][k]])
                    {
                        add(dp[i+1][tr[j][k]][0], dp[i][j][0]);
                        //cout << "dp[" << i+1 << "][" << tr[j][k] << "][0]: " << dp[i+1][tr[j][k]][0] << " <-- " << "dp[" << i << "][" << j << "][0]: " << dp[i][j][0] << ",    tr[" <                         if (k < (s[i]-'0')) {
                            add(dp[i+1][tr[j][k]][0], dp[i][j][1]);
                            //cout << "<<<< dp[" << i+1 << "][" << tr[j][k] << "][0]: " << dp[i+1][tr[j][k]][0] << " <-- " << "dp[" << i << "][" << j << "][1]: " << dp[i][j][1] << ",    tr[" <                         }
                        if (k == (s[i]-'0')) {
                            add(dp[i+1][tr[j][k]][1], dp[i][j][1]);
                            //cout << "==== dp[" << i+1 << "][" << tr[j][k] << "][1]: " << dp[i+1][tr[j][k]][1] << " <-- " << "dp[" << i << "][" << j << "][1]: " << dp[i][j][1] << ",    tr[" <                         }
                    }
                }
                //cout << "\n\n";
            }

    long long ans = 0;
    for (j = 0; j <= tot; ++j)
        if (!e[j])
        {
            add(ans, dp[len][j][0]);
            add(ans, dp[len][j][1]);
        }

    cout << ans - 1 << endl;

    return 0;
}

你可能感兴趣的:(算法,动态规划,数据结构)