暑期专题五 数位DP
1.Bomb
给一个数字\(N\),求\(1\)~\(N\)有多少个数字的序列包含\(“49”\)子序列。
Input
第一行输入由整数\(T(1 <= T <= 10000)\)组成,表示测试用例的数量。对于每个测试用例,将有一个整数\(N(1 <= N <= 2 ^ {63}-1)\)作为描述。
Output
对于每个测试用例,输出一个数字代表1到N有多少个包含49的数字。
思路:一道非常简单的数位DP,采用记忆化搜索实现,只需要记录有多少位,然后当前位是多少即可。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n, p[30], len, num[30], f[25][2];
ll dp(int now, int lim, int hv)
{
if (!now) return 0;
if (lim && ~f[now][hv]) return f[now][hv];
int up = lim ? 9 : num[now];
ll res = 0;
for (int i = 0; i <= up; i++)
{
if (hv == 1 && i == 9) res += lim ? p[now] : (n % p[now] + 1);
else res += dp(now - 1, lim | (i != up),i == 4);
}
return lim ? f[now][hv] = res : res;
}
int main()
{
int T;
scanf("%d", &T);
memset(f, -1, sizeof f);
while (T--)
{
scanf("%lld", &n);
ll n_ = n;
len = 0;
while (n_) num[++len] = n_ % 10, n_ /= 10;
p[1] = 1;
for (int i = 2; i <= len; i++) p[i] = p[i - 1] * 10;
printf("%lld\n", dp(len, 0, 0));
}
return 0;
}
2.F(x)
我们定义十进制数\(x\)的权值为\(f(x) = a(n)*2^{(n-1)}+a(n-1)*2^{(n-2)}+...a(2)*2+a(1)*1\),\(a(i)\)表示十进制数\(x\)中第\(i\)位的数字。 题目给出\(a,b\),求出\(0\)~\(b\)有多少个权值不大于\(f(a)\)的数。
Input
The first line has a number \(T (T <= 10000)\) , indicating the number of test cases.
For each test case, there are two numbers A and B \((0 <= A,B < 10^9)\)
Output
For every case,you should output "Case #t: " at first, without quotes. The t is the case number starting from \(1\). Then output the answer.
思路:同样是非常简单的一道数位DP,但是有一个点,就是如果直接记录权值不太可行,因为记忆化搜索,f的权值在不同限制下方案数显然不同,所以转变思路,f开二维,一维记数位,另一位记剩余可用的权值,这样的话记忆化搜索的结果就可以直接使用了。
#include <bits/stdc++.h>
using namespace std;
int num[15], f[15][6005], p10[15], p2[15];
int dfs(int now, bool lim, int val)
{
if (val < 0) return 0;
if (!now) return 1;
if (!lim && ~f[now][val]) return f[now][val];
int mx = lim ? num[now] : 9, ans = 0;
for (int i = 0; i <= mx; i++) ans += dfs(now - 1, lim && i == mx, val - i * p2[now]);
return lim ? ans : f[now][val] = ans;
}
inline void solve(int t)
{
p10[1] = p2[1] = 1;
for (int i = 2; i <= 10; i++) p10[i] = p10[i - 1] * 10;
for (int i = 2; i <= 10; i++) p2[i] = p2[i - 1] * 2;
int a, b, len = 0, ff = 0;
scanf("%d%d", &a, &b);
while (b) num[++len] = b % 10, b /= 10;
for (int i = 0; a; i++)
{
ff += (1 << i) * (a % 10);
a /= 10;
}
printf("Case #%d: %d\n", t, dfs(len, 1, ff));
}
int main()
{
memset(f, -1, sizeof f);
int t, T = 0;
scanf("%d", &t);
while (t--) solve(++T);
}
3.吉哥系列故事——恨\(7\)不成妻
单身!
依然单身!
吉哥依然单身!
DS级码农吉哥依然单身!
所以,他生平最恨情人节,不管是\(214\)还是\(77\),他都讨厌!
吉哥观察了\(214\)和\(77\)这两个数,发现:
\(2+1+4=7\)
\(7+7=7*2\)
\(77=7*11\)
最终,他发现原来这一切归根到底都是因为和\(7\)有关!所以,他现在甚至讨厌一切和7有关的数!
什么样的数和\(7\)有关呢?
如果一个整数符合下面\(3\)个条件之一,那么我们就说这个整数和\(7\)有关——
1、整数中某一位是\(7\);
2、整数的每一位加起来的和是\(7\)的整数倍;
3、这个整数是\(7\)的整数倍;
现在问题来了:吉哥想知道在一定区间内和\(7\)无关的数字的平方和。
Input
输入数据的第一行是\(case\)数\(T(1 <= T <= 50)\),然后接下来的\(T\)行表示\(T\)个\(case\);每个\(case\)在一行内包含两个正整数\(L, R(1 <= L <= R <= 10^{18})\)。
Output
请计算\([L,R]\)中和\(7\)无关的数字的平方和,并将结果对\(10^9 + 7\)求模后输出。
思路:相比前面的题,这个题的难度就有点大了。首先满足没有任何一位是\(7\)是非常简单的,那么如何处理后面两个限制呢,记忆化搜索的时候就记录一下每一位的和除以\(7\)的余数,以及这个整数除以\(7\)的余数,然后平方和的话就要处理一下一次方的和,然后稍微转化一下就行了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
struct dig {
ll x, y, z;
}f[105][105][105];
ll p[105], num[105];
dig dfs(ll now, ll x, ll y, bool lim)
{
if (!now) return dig{0, 0, x && y};
if (!lim && ~f[now][x][y].z) return f[now][x][y];
ll mx = lim ? num[now] : 9;
dig res = dig{0, 0, 0};
for (ll i = 0; i <= mx; i++)
{
if (i == 7) continue;
dig tmp = dfs(now - 1, (x + i) % 7, (y * 10 + i) % 7, lim && i == mx);
res.z = (res.z + tmp.z) % mod;
res.x = (res.x + tmp.x + i * p[now] % mod * tmp.z % mod) % mod;
res.y = (res.y + tmp.y + 2 * i * p[now] % mod * tmp.x % mod) % mod;
res.y = (res.y + tmp.z * i % mod * p[now] % mod * i % mod * p[now] % mod) % mod;
} return !lim ? f[now][x][y] = res : res;
}
ll cal(ll x)
{
ll len = 0;
if (!x) return 0;
while (x) num[++len] = x % 10, x /= 10;
dig tmp = dfs(len, 0, 0, 1);
return tmp.y;
}
inline void solve()
{
ll a, b;
scanf("%lld%lld", &a, &b);
printf("%lld\n", (cal(b) - cal(a - 1) + mod) % mod);
}
signed main()
{
memset(f, -1, sizeof f);
p[1] = 1;
for (ll i = 2; i <= 100; i++) p[i] = p[i - 1] * 10 % mod;
ll t;
scanf("%lld", &t);
while (t--) solve();
}
4.XHXJ's LIS
给定一个区间中,将区间的每一个数看成一个字符串,求这个区间内每个字符串的最大上升子序列等于\(k\)的个数。
Input
First a integer \(T(T<=10000)\),then \(T\) lines follow, every line has three positive integer \(L,R,K.( 0<L<=R<2^{63}-1\) and \(1<=K<=10).\)
Output
For each query, print "Case #t: ans" in a line, in which t is the number of the test case starting from 1 and ans is the answer.
思路:如果最长上升子序列里面没有比当前数还大的,直接将当前数插入即得到新的最长上升子序列,否则我们找到比当前数大的所有数里最小的,用当前数替换即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll k, f[25][10005][11], num[25];
ll cnt(ll x)
{
ll ans = 0;
while (x)
{
if (x & 1) ++ans;
x >>= 1;
}
return ans;
}
ll pos(ll x, ll now)
{
for (ll i = now; i <= 9; i++) if (x & (1 << i)) return ((x ^ (1 << i)) | (1 << now));
return (x | (1 << now));
}
ll dfs(ll now, ll p, bool lim, bool ld)
{
if (!now) return cnt(p) == k;
if (!lim && ~f[now][p][k]) return f[now][p][k];
ll mx = lim ? num[now] : 9, ans = 0;
for (ll i = 0; i <= mx; i++) ans += dfs(now - 1, ld && !i ? 0 : pos(p, i), lim && i == mx, ld && !i);
return !lim ? f[now][p][k] = ans : ans;
}
ll cal(ll x)
{
ll len = 0;
while (x) num[++len] = x % 10, x /= 10;
return dfs(len, 0, 1, 1);
}
inline void solve(ll t)
{
ll a, b;
scanf("%lld%lld%lld", &a, &b, &k);
printf("Case #%lld: %lld\n", t, cal(b) - cal(a - 1));
}
signed main()
{
memset(f, -1, sizeof f);
ll t, T = 0;
scanf("%lld", &t);
while (t--) solve(++T);
}
5.Zhu’s Math Problem
Zhu is a powerful ACMer/OverWatcher, now a salt-fish comes to ask Zhu a difficult problem. Zhu thinks that problem is so easy, so he wants to know whether you can solve it?
The problem content are as follows.
You are given four integers \(A , B , C , D\) , there are many different \((a,b,c,d)\) satisfy \(a+c>b+d\) && \(a+d≥b+c\) && \(0≤a≤A\) && \(0≤b≤B\) && \(0≤c≤C\) && \(0≤d≤D\) ?
Input
First Line is an positive integer \(T(T \leq 1000)\) , represents there are \(T\) test cases.
Four each test:
The first line contain four integers \(A , B , C , D.\)
You can assume that \(0 \leq A,B,C,D \leq 10^{18}\)
Output
For each test case, output an positive integer represents answer, because the answer may be large , so you are only need to output answer mod \(10^9+7\)
思路:略加思考的话,不难发现一个结论,就是判断\(a+b\)与\(c+d\)的大小关系时,如果从高到低有一位两个数的差大于等于2,那么两个数的大小关系就已确定。因为低位进位最多只能进\(1\)。有了这个性质,我们直接暴力枚举\(a+c-b-d\)和\(a+d-b-c\)就行了。但是这样的话时空都无法承受,于是考虑转化为二进制来枚举,单次枚举的时间复杂度就从\(O(10^4)\)减少到\(O(2^4)\)了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
ll f[67][4][4][16], num2[67], num3[67], num4[67], num1[67];
ll dfs(ll now, ll c1, ll c2, ll lim)
{
if (!now) return c1 > 0 && c2 >= 0;
if (~f[now][c1 + 1][c2 + 1][lim]) return f[now][c1 + 1][c2 + 1][lim];
ll mx1 = (lim & 8) ? num1[now] : 1, mx2 = (lim & 4) ? num2[now] : 1, mx3 = (lim & 2) ? num3[now] : 1, mx4 = (lim & 1) ? num4[now] : 1, ans = 0;
for (ll a = 0; a <= mx1; a++) for (ll b = 0; b <= mx2; b++) for (ll c = 0; c <= mx3; c++) for (ll d = 0; d <= mx4; d++)
{
ll tmp1 = min(2ll, c1 * 2 + a + c - b - d), tmp2 = min(2ll, c2 * 2 + a + d - b - c), l = 0;
if (tmp1 <= -2 || tmp2 <= -2) continue;
if (a == mx1) l |= 8; if (b == mx2) l |= 4; if (c == mx3) l |= 2; if (d == mx4) l |= 1;
ans = (ans + dfs(now - 1, tmp1, tmp2, lim & l)) % mod;
} return f[now][c1 + 1][c2 + 1][lim] = ans;
}
inline void solve()
{
memset(f, -1, sizeof f);
ll a, b, c, d, len = 0;
scanf("%lld%lld%lld%lld", &a, &b, &c, &d);
while (a || b || c || d)
{
num1[++len] = (a & 1), a >>= 1;
num2[len] = (b & 1), b >>= 1;
num3[len] = (c & 1), c >>= 1;
num4[len] = (d & 1), d >>= 1;
} printf("%lld\n", dfs(len, 0, 0, 15));
}
signed main()
{
ll t;
scanf("%lld", &t);
while (t--) solve();
}
6.Balanced Numbers
Balanced numbers have been used by mathematicians for centuries. A positive integer is considered a balanced number if:
-
Every **even** digit appears an **odd** number of times in its decimal representation
-
Every **odd** digit appears an **even** number of times in its decimal representation
For example, \(77\), \(211\), \(6222\) and \(112334445555677\) are balanced numbers while \(351\), \(21\), and \(662\) are not.
Given an interval \([A, B]\), your task is to find the amount of balanced numbers in \([A, B]\) where both \(A\) and \(B\) are included.
Input
The first line contains an integer \(T\) representing the number of test cases.
A test case consists of two numbers A and B separated by a single space representing the interval. You may assume that 1 <= A <= B <= 1019
Output
For each test case, you need to write a number in a single line: the amount of balanced numbers in the corresponding interval
思路:解法较为新颖,对于每一个数出现的次数,我们用三进制的状态来表示。其中\(0\)代表没有出现过,\(1\)代表出现了奇数次,\(2\)代表出现了偶数次,那么这个状态就很好转移了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll num[25], tmp[25], p[25], f[25][100005];
ll get(ll val, ll x)
{
for (ll i = 0; i <= 9; i++) tmp[i] = val % 3, val /= 3;
if (tmp[x] == 1) tmp[x] = 2;
else tmp[x] = 1;
ll ans = 0;
for (ll i = 9; i >= 0; i--) ans = ans * 3 + tmp[i];
return ans;
}
bool judge(ll val)
{
for (ll i = 0; i <= 9; i++) tmp[i] = val % 3, val /= 3;
for (ll i = 0; i <= 9; i++)
{
if ((tmp[i] == 1) && (i % 2)) return 0;
if ((tmp[i] == 2) && (i % 2 == 0)) return 0;
} return 1;
}
ll dfs(ll now, ll val, bool lim, bool ld)
{
if (!now) return judge(val);
if (!lim && ~f[now][val]) return f[now][val];
ll mx = lim ? num[now] : 9, ans = 0;
for (ll i = 0; i <= mx; i++) ans += dfs(now - 1, (ld && !i) ? 0 : get(val, i), lim && i == mx, ld && !i);
// printf("%d\n", ans);
return !lim ? f[now][val] = ans : ans;
}
ll cal(ll x)
{
ll len = 0;
while (x) num[++len] = x % 10, x /= 10;
return dfs(len, 0, 1, 1);
}
inline void solve()
{
ll a, b;
scanf("%lld%lld", &a, &b);
printf("%lld\n", cal(b) - cal(a - 1));
}
signed main()
{
memset(f, -1, sizeof f);
ll t;
scanf("%lld", &t);
while (t--) solve();
}
原文地址:https://www.cnblogs.com/Chasing-Dreams-Z/p/15172785.html
- 解决网站静态缓存后WP-PostViews插件不计数的问题
- Haproxy安装部署文档及多配置文件管理方案
- ASM 翻译系列第三十四弹:ASM磁盘组重要属性介绍
- 博客集成Hitokoto·一言经典语句功能
- 博客网页导致电脑CPU飙升的问题解决记录
- 恢复WordPress分类目录的别名链接形式
- 替代crontab,统一定时任务管理系统cronsun简介
- 小网站最简单实用的动静分离优化方案
- Haproxy进阶管理:命令行控制后端节点上下线
- 网站集成打字震动特效JS代码改进版
- Linux基础知识之文件隐藏属性
- Linux系统chmod误操作目录权限恢复方法
- 结合VBS,实现批处理自动以管理员身份执行
- ASM 翻译系列第三十六弹:ACFS磁盘组的重平衡操作
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 解决java的http请求库dongliu.requests请求结果中文乱码的问题
- 保姆级教程,手把手教你实现一个SpringBoot的starter
- 微服务[学成在线] day05:消息中间件 RabbitMQ
- 微服务[学成在线] day06:页面发布以及课程管理
- 微服务[学成在线] day07:课程管理开发
- 微服务[学成在线] day08:FastDFS 实现课程图片管理
- 【MySQL入门】之细说脏读、幻读及不可重复读
- 微服务[学成在线] day09:Eureka、Feign、课程预览实现
- 【MySQL入门】之MySQL数据库的锁机制(一)
- 【MySQL入门】之MySQL数据库的锁机制(二)
- 【MySQL】删库别着急跑路(一)--Xtrabackup的原理及使用
- 微服务[学成在线] day11:基于 ElasticSearch 构建搜索服务
- 【MySQL】删库别着急跑路(二)--mysqldump的使用
- 【MySQL】删库别着急跑路(三)--binlog2sql闪回工具的使用
- 微服务[学成在线] day10:课程发布、ElasticSearch