问题描述
windy 定义了一种 windy 数。不含前导零且相邻两个数字之差至少为 2 的正整数被称为 windy 数。windy 想知道在 A 和 B 之间(包括 A 和 B)总共有多少个 windy 数?
输入
包含两个整数,A B。
输出
一个整数,同题目要求
思路
一个比较简单的数位DP吧,直接按照套路来就好了。$f[i][j]$ 表示在第 $i$ 位上为 $j$ 的可能情况数,利用 $f$ 数组记忆化。然后需要注意的是,处理前导零时也不能够记忆化(仅针对我这种写法)。
代码
1  | 
  | 
windy 定义了一种 windy 数。不含前导零且相邻两个数字之差至少为 2 的正整数被称为 windy 数。windy 想知道在 A 和 B 之间(包括 A 和 B)总共有多少个 windy 数?
包含两个整数,A B。
一个整数,同题目要求
一个比较简单的数位DP吧,直接按照套路来就好了。$f[i][j]$ 表示在第 $i$ 位上为 $j$ 的可能情况数,利用 $f$ 数组记忆化。然后需要注意的是,处理前导零时也不能够记忆化(仅针对我这种写法)。
1  | #include <cstdio>  | 
Tehran 的一家每天 $24$ 小时营业的超市,需要一批出纳员来满足它的需要。超市经理雇佣你来帮他解决问题——超市在每天的不同时段需要不同数目的出纳员为顾客提供优质服务。他希望雇佣最少数目的出纳员。
经理已经提供给你一天的每一小时需要出纳员的最少数量——$R(0),R(1),⋯,R(23)$。表示从午夜到上午 $0:00$ 需要出纳员的最小数目,每一天,这些数据都是相同的。有 $N$ 人申请这项工作,每个申请者 $i$ 在每 $24$ 小时中,从一个特定的时刻开始连续工作恰好 $8$ 小时。定义 $t_i$ 为上面提到的开始时刻。也就是说,如果第 $i$ 个申请者被录取,他将从 $t_i$ 时刻开始连续工作 $8$ 小时。
第一行为测试点的数目 $T$。
对于每组测试数据,第一行为 $24$ 个整数,表示 $R(0),R(1),R(2),⋯ ,R(23)$ 接下来一行一个正整数 $N$,表示申请者数目; 接下来 $N$ 行每行一个整数 $t_i$
对于每个测试点,输出一行,包含一个整数,表示需要出纳员的最小数目。如果无解,输出 No Solution。
一道差分约束很好的例题。本题思路引用了 这篇 Discuss
首先考虑约束关系。设:$r_i$ 为时间 $i$ 需要的人数 $t_i$ 为时间 $i$ 应聘的人数 $s_i$ 为时间 $i$ 以及之前录用的人数和
那么就存在约束关系: $s_i-s_{i-8}\geq r_i (8\leq i\leq 24)$ $s_i-s_{16+i}\geq r_i-s_{24} (1\leq i\leq 7)$ $s_i-s_{i-1}\geq 0 (1\leq i\leq 24)$ $s{i-1}-s_i\geq -t_i (1\leq i\leq 24)$
1  | #include <cstdio>  | 
$n$只奶牛构成了一个树形的公司,每个奶牛有一个能力值$p_i$,$1$号奶牛为树根。问对于每个奶牛来说,它的子树中有几个能力值比它大的。
第一行一个整数$n$,表示有$n$头奶牛 接下来$n$行为$1-n$号奶牛的能力值$p_i$ 接下来$n-1$行为$2-n$号奶牛的经理(树中的父亲)
共$n$行,每行输出奶牛$i$的下属中有几个能力值比$i$大
其实看完题后第一反应是树上启发式合并(不过应该可做,这种方法先坑着)。既然这是线段树合并的模版,那么还是先用这种方法做一遍吧。
首先离散化,然后对于每一个节点都建一颗权值线段树(里面都只有一个节点),接着dfs一遍,将每个节点儿子的线段树并在该节点上,然后跟新这个节点的答案就好了。
不过合并的操作复杂度是取决于线段树重合部分的大小,空间也要开很多倍。
1  | #include <cstdio>  | 
·9月4日 Codeforces Round #505 题目链接:http://codeforces.com/contest/1025
过水,就不说了。。
思路: 首先我们不难想到将每一对数乘起来,然后求这些乘积的GCD,如果这个GCD为1则应该输出-1。但是这个GCD不一定是答案,但答案一定是这个GCD的约数。这时我就想到了找这个GCD最小的约数做为答案,但这样是会T的。于是我们考虑造成GCD不一定是答案的原因,然后我们可以将这个GCD与每一对中的一个数再求一遍GCD,这样的答案就是符合要求的了。
代码:
1  | #include <bits/stdc++.h>  | 
思路: 一开始什么思路都没有,卡了很久。后面有一种直觉就是如果头和尾相等,那么如何翻转都是不会增加答案的(虽然一直不会证明)。
设$1-i$的字符串为’zebra’,这样的话只要头尾不相等,在位置$i$进行一次操作,这样’zebra’的长度一定会增加。由于开始提到的‘直觉’,只要操作后头和尾相等就break出循环。
后面看了题解后,就更加奇怪了,不明白自己这种乱搞的做法为什么是对的。
代码:
1  | #include <bits/stdc++.h>  | 
思路: 其实这和平衡树并没有半毛钱关系,只是根据BST的性质让你做区间DP。
对于一个递增序列,你可以任意选一个数作为BST的根,这个数将序列分为两部分,分别作为这个根的左子树和右子树。然后这两个部分又是递增序列,于是可以这样递归下去DP。
大致的思路是这样,但要注意一些细节。首先是要记忆化以保证复杂度,然后就是要记录自己序列是在左子树还是右子树,以便判断是否符合要求(也就是边两端的数GCD>1)。
代码:
1  | #include <bits/stdc++.h>  | 
我们定义某天的最小波动值为$min \lbrace |$该天以前某一天的营业额$-$该天营业额$| \rbrace $
第一天的最小波动值为第一天的营业额
你的任务是编写一个程序帮助Tiger来计算这一个值:每一天的最小波动值之和。
第一行为正整数$n(n<=32767)$,表示该公司从成立一直到现在的天数,接下来的$n$行每行有一个整数$ai(|ai|<=1000000)$,表示第$i$天公司的营业额,可能存在负数
仅一个整数,即每一天的的最小波动值之和(结果小于$2^{31}$)
我们就是要实现 动态插点,找前驱和后继 这三种操作。首先平衡树肯定是能够做到的,于是就码了一发Splay。
然后其实权值线段树也可以做到。我们先将这些值离散化,每个值对应一个叶子节点。于是我们对于每一个节点维护区间内最左边和最右边的位置就好了。
Splay代码:
1  | #include <bits/stdc++.h>  | 
权值线段树代码:
1  | #include <cstdio>  |