题意:

给出一个 nnn,现可以从 111 ~ nnn 中任意选取若干个数字,要求所有数字不存在重复的数位。例如 {1,2,31,2,31,2,3} 和 {2,112,112,11} 是合法的,但是 {1,2,101,2,101,2,10} 和 {2,5,122,5,122,5,12} 是不合法的。

现给出一个 nnn,询问存在多少个集合满足上述要求,结果模 1e9+71e9+71e9+7。(1≤n≤109)(1\leq n\leq 10^9)(1≤n≤109)


思路:

首先,不难想到此题需要将 000 ~ 999 的数位状压起来,然后再进行数位 dpdpdp 求解,最后再进行一个类似于容斥或背包的操作求出答案。

我们定义 num[i]num[i]num[i] 表示 111 ~ nnn 中有多少个数字,数字中包含的000 ~ 999 的状态恰好为 iii。再定义 ans[i]ans[i]ans[i] 表示 111 ~ nnn 中有多少个不同的满足条件的集合,集合中数位的状态恰好为 iii。

因此我们先求 num[i]num[i]num[i],定义数位 dpdpdp 的状态为 dp[S][pos]dp[S][pos]dp[S][pos] 表示长度为 pospospos 的数位,数位状态为 SSS 的数的个数。

然后数位 dpdpdp 经典操作 dfsdfsdfs 求取 num[i]num[i]num[i],中间用 dp[S][pos]dp[S][pos]dp[S][pos] 进行记忆化搜索。枚举第 pospospos 位的数位时,答案既可以由 dp[S][pos−1]dp[S][pos-1]dp[S][pos−1] 更新而来,也可以由 dp[Sxor(1&lt;&lt;i)][pos−1]dp[S\ xor\ (1&lt;&lt;i)][pos-1]dp[S xor (1<<i)][pos−1] 更新而来。因为 pospospos 位已经提供了 iii 这个数位,因此之后的数位可以提供也可以不提供,因此有两种情况。

所以我们可以只用这个二维的状态即可求解出 num[i]num[i]num[i]。比赛时数位 dpdpdp 部分写了一个比较复杂的三维状态,最后卡时通过。

然后我们再来考虑如何求取 ans[i]ans[i]ans[i]。两种考虑方法,一种是利用的背包的想法,一种是利用组合的想法。

先讲背包的想法。我们已经求出了 num[i]num[i]num[i] 表示 111 ~ nnn 中数位状态为 iii 的数的个数。然后可以将 num[i]num[i]num[i] 中的每一个 iii 看成一个物品,然后 ans[i][j]ans[i][j]ans[i][j] 表示利用前 iii 个物品组成的状态为 jjj 的方案总数。

ans[0][0] = 1;
for(nt i = 0; i < 1024; i++)for(int j = 0; j < 1024; j++){ans[i+1][j] += ans[i][j];if((j&i) == 0){ans[i+1][j|i] += ans[i][j] * num[i];}}

再讲组合容斥的想法。定义 ans[i][j]ans[i][j]ans[i][j] 表示有多少个集合,组成了 iii 这个状态,一共含有 jjj 个不同的数位状态。比如数位 121212 这个状态,既可以由 111 和 222 两个状态组成,也可以直接由 121212 这一个状态组成。然后我们就可以直接枚举子集进行计算。这里需要注意,枚举子集时,每次由一个单体和一个集合一起算贡献,而不是每次用两个集合算贡献,这样可以尽量避免重复。利用单体算贡献,每个单体被重复计算 kkk 次,因此最后答案除 kkk。

rep(S, 1, endS){rep(k, 2, 9){ll cnt = 0;for(int sub = S & (S - 1); sub; sub = (sub - 1) & S){int x = sub, y = S - sub;cnt = (cnt + DP[x] * ans[y][k - 1]) % mod;}cnt = cnt * inv[k] % mod;ans[S][k] = (ans[S][k] + cnt) % mod;}}

代码:

#include <bits/stdc++.h>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define mem(a,b) memset(a,b,sizeof a);
#define rep(i, a, b) for(int i = a; i <= b; i++)
#define LOG1(x1, x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1, x2, y1, y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
#define LOG3(x1, x2, y1, y2, z1, z2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << " , " << z1 << ": " << z2 << endl;
typedef long long ll;
typedef double db;
const db EPS = 1e-9;
const ll mod = 1e9 + 7;
using namespace std;ll dp[1 << 11][13], endS, ans[1 << 11][10], DP[1 << 11], inv[100];
int len, a[13];void init() {mem(dp,-1);mem(ans,0);mem(DP,0);
}ll dfs(int pos, int state, bool flag, int jud) { //jud表示有无数字if (pos == 0) {if(state == 0 && jud == 1) return 1;else return 0;}if (!flag && dp[state][pos] != -1 && jud == 1) return dp[state][pos];ll base = 0;int end = flag ? a[pos] : 9;rep(i, 0, end){if(jud == 0 && i == 0){ //无数字base = (base + dfs(pos - 1, state, flag && i == end, 0)) % mod;}else{if (((1 << i) & state) == 0) continue;base = (base + dfs(pos - 1, state, flag && i == end, 1)) % mod;base = (base + dfs(pos - 1, state^(1<<i), flag && i == end, 1)) % mod;}}if (!flag && jud == 1) dp[state][pos] = base;return base;
}void solve(ll n) {len = 0;memset(a, 0, sizeof a);while (n) {a[++len] = n % (10ll);n /= 10ll;}rep(S, 1, endS) DP[S] = dfs(len,S,1,0);
}ll pow_mod(ll a, ll b, ll m) {ll ans = 1;while (b) {if (b & 1)ans = ans * a % mod;a = a * a % mod;b >>= 1;}return ans;
}int main() {int _;scanf("%d", &_);for (int i = 1; i <= 100; ++i) {inv[i] = pow_mod(i, mod - 2, mod);}endS = (1 << 11) - 1;rep(Ca, 1, _) {init();ll n;scanf("%lld", &n);solve(n);rep(S, 1, endS) ans[S][1] = DP[S];rep(S, 1, endS){rep(k, 2, 9){ll cnt = 0;for(int sub = S & (S - 1); sub; sub = (sub - 1) & S){int x = sub, y = S - sub;cnt = (cnt + DP[x] * ans[y][k - 1]) % mod;}cnt = cnt * inv[k] % mod;ans[S][k] = (ans[S][k] + cnt) % mod;}}ll sum = 0;rep(S, 1, endS) rep(i, 1, 9) sum = (sum + ans[S][i]) % mod;printf("Case %d: %lld\n", Ca, sum);}return 0;
}

【UVALive - 7344】Numbered Cards【数位DP+状压DP】相关推荐

  1. 【BZOJ】1076 [SCOI2008]奖励关 期望DP+状压DP

    [题意]n种宝物,k关游戏,每关游戏给出一种宝物,可捡可不捡.每种宝物有一个价值(有负数).每个宝物有前提宝物列表,必须在前面的关卡取得列表宝物才能捡起这个宝物,求期望收益.k<=100,n&l ...

  2. [转]状态压缩dp(状压dp)

    状态压缩动态规划(简称状压dp)是另一类非常典型的动态规划,通常使用在NP问题的小规模求解中,虽然是指数级别的复杂度,但速度比搜索快,其思想非常值得借鉴. 为了更好的理解状压dp,首先介绍位运算相关的 ...

  3. SCUT - 254 - 欧洲爆破 - 概率dp - 状压dp

    https://scut.online/p/254 思路很清晰,写起来很恶心. #include<bits/stdc++.h> using namespace std; #define l ...

  4. bzoj4455 loj2091 [Zjoi2016]小星星 容斥原理+树形DP(+状压DP?)

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4455 https://loj.ac/problem/2091 题解 很不错的一道题.(不过在当 ...

  5. 天上掉馅饼 期望dp+状压dp

    天上掉馅饼 (bonus) 题目描述 小G进入了一个神奇的世界,在这个世界,天上会掉下一些馅饼.今天,天上会随机掉下k个馅饼. 每次天上掉下馅饼,小G可以选择吃或者不吃(必须在下一个馅饼掉下来之前作出 ...

  6. 状压dp个人刷题记录

    目录 一.普通型 蒙德里安的梦想 题意: 思路: code: #2153. 「SCOI2005」互不侵犯 题意: 思路: code: P1879 [USACO06NOV]Corn Fields G 题 ...

  7. LOL UVALive - 8521 —— 状压DP

    This way 题意: 现在有100个英雄,有5个人打人机正在进行办选局,5个人拥有的英雄都以01的方式告诉你,每个人的办选不同视为不同情况,也就是说玩家1选了德莱文和玩家二选了德莱文是两种情况,玩 ...

  8. 【思维题 状压dp】APC001F - XOR Tree

    可能算是道中规中矩的套路题吧-- Time limit : 2sec / Memory limit : 256MB Problem Statement You are given a tree wit ...

  9. 【2019牛客暑期多校训练营(第五场)- E】independent set 1(最大独立集,状压dp)

    题干: 链接:https://ac.nowcoder.com/acm/contest/885/E 来源:牛客网 Note: For C++ languages, the memory limit is ...

  10. [状压dp] 最短Hamilton路径(模板题+状压dp)

    文章目录 0. 前言 1. 状压dp 模板题 0. 前言 状压 dp 就是采用二进制数保存状态,方便进行位运算操作.例如 八皇后.八数码问题也都是采用了状态压缩的思想来使用一个二进制数唯一对应集合中的 ...

最新文章

  1. 当前linux环境做出镜像,把当前ubuntu系统做成镜像
  2. laravel中Crypt加密方法
  3. 引入科研院所中科微研携手-林裕豪:从玉农业谋定农业大健康
  4. JavaScript window.getComputedStyle()
  5. Linux命令之sftp - 安全文件传输命令行工具
  6. 【数据结构与算法】之判断一个整数是否是 4 的幂次方的高逼格算法
  7. (转)Inno Setup入门(六)——在程序目录下创建文件夹
  8. 内网服务器文件如何加密,局域网共享文件如何加密?
  9. C语言入栈算法,栈的入栈、出栈、获取栈顶的c语言算法
  10. [导入]ASP.NET AJAX 说明文档-客户端引用-全局命名空间-JavaScript 基础类型扩展-Array 类型扩展...
  11. 【数据预测】基于matlab BP+ELM+LSTM+BiLSTM+SAELSTM数据预测【含Matlab源码 1825期】
  12. 为什么我特别讨厌语音输入
  13. 软件开发流程(Software development process)
  14. 红米note7android10,红米Note7 Pro 安卓10.0原生刷机包(最新固件升级包lineage17.1)
  15. yolov3gpu配置_YOLO3-WIN10-GPU版配置详细教程
  16. 《三国演义》里到底描写了多少个人物,你知道吗?
  17. vbv参数buffer init
  18. 商品绑定可用的优惠券(多对多的绑定且一张优惠券只能使用于一个商品)
  19. Linux系统下Jsp验证码显示不出来, nginx 返回500 解决方法
  20. escape的主要用途

热门文章

  1. Linq to sql 语法方法示例
  2. python struct_struct
  3. Linux学习笔记6 - 用户和组群账户管理
  4. servlet-mapping_浅谈servletmapping的机制(二)
  5. uid_t gid_t等的定义
  6. java中printreader类_java字符流,字符文件输入流FileReader类介绍
  7. 计算机硬件不仅使用二进制,【判断题】计算机硬件中不仅使用二进制表示数据,也经常使用十六进制。...
  8. 2019蓝桥:2019拆分为平方和问题
  9. 非线性优化:Ax=b求解的几种算法
  10. 如你以安全模式启动计算机,如何以安全模式启动计算机?