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;
}
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号