codeforces 258B

本文探讨了一种使用数位DP预处理技术解决特定组合问题的方法,详细介绍了题目的背景、解决方案及其优化过程。

很经典的思路啊,比赛的时候没怎么多想,其实还很简单的。
题意:有7个party,然后有m个数字,1-m,每个party可以选择其中一个数字,4和7是幸运的字符,每个数字有多个幸运字符比如4447有四个。
问你第一个party选的数字的幸运字符个数 严格大于其他party选择的数字的幸运字符个数的和 的选择方法数。

用数位dp预处理出1-m之间幸运字符个数为i数字数为c[i],然后枚举第一个party选择的i,然后深搜其他party,在满足条件的情况下,全排列即可,最后求个和

//
//  Created by Matrix on 2016-01-22
//  Copyright (c) 2015 Matrix. All rights reserved.
//
//
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <sstream>
#include <set>
#include <vector>
#include <stack>
#define ALL(x) x.begin(), x.end()
#define INS(x) inserter(x, x,begin())
#define ll long long
#define CLR(x) memset(x, 0, sizeof x)
using namespace std;
const int inf = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int maxn = 1e6 + 10;
const int maxv = 1e3 + 10;
const double eps = 1e-9;

ll pow_mod(ll a, ll n) {
    ll ret = 1;
    while(n) {
        if(n & 1) ret = ret * a % MOD;
        a = a * a % MOD;
        n >>= 1;
    }
    return ret;
}
int digit[55];
ll c[10];
ll dp[55][20][20];
ll dfs(int step, int limit, int cnt, int ju) {
    if(step == 0) {
        return cnt == ju;
    }
    if(!limit && dp[step][cnt][ju] != -1) return dp[step][cnt][ju];
    ll ret = 0;
    int up = limit ? digit[step] : 9;
    for(int i = 0; i <= up; i++) {
        ret += dfs(step-1, limit && i == up, cnt + (i == 4 || i == 7 ? 1 : 0), ju);
    }
    if(!limit) dp[step][cnt][ju] = ret;
    return ret;
}
ll A(ll n, ll a) {
    ll ret = 1;
    if(a > n) return 0;
    while(a--) {
        ret = ret * n % MOD;
        n--;
    }
    return ret;
}
ll calc(ll n, int ju) {
    memset(dp, -1, sizeof dp);
    int top = 0;
    while(n) {
        digit[++top] = n % 10;
        n /= 10;
    }
    return dfs(top, 1, 0, ju);
}
ll res;
void DFS(int step, int sum, int ju) {
    if(step == 6) {
        ll cnt = 1;
        for(int i = 0; i < ju; i++) {
            if(digit[i]) {
                cnt = cnt * A(c[i], digit[i]) % MOD;
            }
        }
//      printf("ju = %d  cnt = %lld\n", ju, cnt);
        res += cnt;
        return;
    }
    for(int i = 0; i + sum < ju; i++) {
        if(digit[i] >= c[i]) continue;
        digit[i]++;
        DFS(step+1, sum + i, ju);
        digit[i]--;
    }
}
int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    ll m;
    while(cin >> m) {
        CLR(c);
        for(int i = 0; i <= 9; i++) {
            c[i] = calc(m, i);
        }
        c[0]--;
//      for(int i = 0; i <= 9; i++) {
//          printf("c[%d] = %lld\n", i, c[i]);
//      }
        ll ans = 0;
        for(int i = 1; i <= 9; i++) {
            if(!c[i]) continue;
            res = 0;
            CLR(digit);
            DFS(0, 0, i);
            ans = (ans + res * c[i] % MOD) % MOD;
        }
        printf("%lld\n", ans);
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值