cf258 B. Little Elephant and Elections(数位dp+dfs)

题意:

7个人在[1,m]区间内取数,每人取一个数,不能取相同数。求第一个人选的数中的4和7的个数大于其他六个人的4和7的个数总和的方案数。

\(7\le m \le 1e9\)

思路:

\(m\)\(n\) 位数字,\(d_i\)\(m\) 的第 \(i\) 位数字。最左边是第一位。

\(dp(i,j,1)\) 表示 \(m\) 的前 \(i\) 位数字(即 \(\overline{d_1d_2\cdots d_i}\))中是不是恰有 \(j\) 位4或7。这个很好算,先算出来。

\(dp(i,j,0)\) 表示所有严格小于 $\overline{ m的前 i 位} $ 的数中,恰有 \(i\) 位数字是4或7的数的个数,\(j\in [0,i]\)

初值:\(dp(1,0,0)=小于d_1的数中有几个数既不是4又不是7,dp(1,1,0)=小于d_1的数中有几个数等于4或7\)

转移:\(dp(i,j,0)=8*dp(i-1,j,0)+(小于d_1的数中有几个数既不是4又不是7)*dp(i-1,j,1)\)

\(+2*dp(i-1,j-1,0)+(小于d_1的数中有几个数等于4或7)*dp(i-1,j-1,1)\)

\(j=0\) 的话就不用后面那行

然后计算 \([1,m]\) 中,4或7的个数小于等于 \(j\) 的数的个数: \(c_j=dp(n,j,0)+dp(n,j,1)\)

注意整数0会被计算到 \(c_0\) 里面,要减去。

接下来dfs就比较简单了。从1到n枚举第一个人选的数中4或7的个数 \(i\)\(dfs(2,i-1)\) 返回其他6个人选的4和7的个数总和小于 \(i\) 的方案数。\(ans=\sum \limits _{1\le i\le n} c_i*dfs(2,i-1)\)

const int N = 11, MOD = 1e9 + 7;
int n;
char d[N];

int c[N];
void init()
{
    static int dp[N][N][2];

    static int L[N], U[N]; //小于d[i]的4或7的个数(一位数);其他数的个数
    for(int i = 1; i <= n; i++) //预处理L[],U[]
    {
        if(d[i] > '7') L[i] = 2; else if(d[i] > '4') L[i] = 1;
        U[i] = d[i]-'0' - L[i];
    }

    //先算全部dp[][][1]
    for(int i = 1, cnt = 0; i <= n; i++)
    {
        cnt += (d[i] == '4' || d[i] == '7');
        dp[i][cnt][1] = 1;
    }
    //初值
    dp[1][0][0] = U[1], dp[1][1][0] = L[1];
    //计算dp[][][0]
    for(int i = 2; i <= n; i++)
        for(int j = 0; j <= i; j++)
        {
            dp[i][j][0] = 8 * dp[i-1][j][0] + U[i] * dp[i-1][j][1];
            if(j) dp[i][j][0] += 2 * dp[i-1][j-1][0] + L[i] * dp[i-1][j-1][1];
        }

    //计算c[]
    for(int j = 0; j <= n; j++)
        c[j] = dp[n][j][0] + dp[n][j][1];
    c[0]--;
}

ll dfs(int id, int sum) //当前是第几个人,47个数的上限
{
    if(id > 7) return 1;

    ll res = 0;
    for(int i = 0; i <= sum; i++) if(c[i]) //枚举这个人有几个47
    {
        res += (c[i]--) * dfs(id + 1, sum - i) % MOD; //选了要-1
        res %= MOD;
        c[i]++; //加回来
    }
    return res;
}

signed main()
{
    cin >> d + 1; n = strlen(d + 1);

    init();

    ll ans = 0;
    for(int i = 1; i <= n; i++) //枚举第一个人选的数中4或7的个数
        ans += c[i] * dfs(2, i-1) % MOD,
        ans %= MOD;

    cout << ans;
}

posted @ 2022-02-06 19:21  Bellala  阅读(41)  评论(0)    收藏  举报