再经过"YE5"和"N0"后又一次被牛客恶心到辣,为什么是对1e7+7取模啊喂!
E明天再补罢我懒了。
(更新:已补E)
前置知识:排序+贪心
问题解析
每次我们可以合并至少两个数,并获得等同于他们之和的分数总和。
那么对于贪心来说,我们可以先对所有数进行降序排序(大的在前面,小的在后面)。
每次合并最大的两个数即可,而因为每次合并后数组最大的两个数肯定是原数组最大的数和当前合并产生的数。
我们可以用一个变量sum记录每次合并后的值,下一次合并只要把数组剩下的最大的值加到sum上,就算完成合并了,每次合并后把sum加到我们的结果上(我这里用一个变量res来存储结果)。
我们就持续合并,直到数组只剩下一个数,或者合并后sum会小于0为止,最后输出结果(res)。
AC代码
#include
using namespace std;
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
问题解析
题目说,只要有一个debuff我们没有拿到,那么我们就可以战胜boss。那么我们可以去枚举每个debuff,看哪个debuff我们不要它后,得到的分数依然是最大的。(这里指的debuff是1~m中某个具体的数)
而我们不要哪个数,说明只要包含这个数的房间我们都不能进。
拿样例来说:
输入
4 5
1 3 30
2 2 40
2 5 80
2 4 60
输出
180
我们可以按照每个房间的区间,把分数加到这些数上。
还是样例: 数字1~ 3都加上30,数字2~ 2都加上40,数字2~ 5都加上80,数字2~ 4都加上60.
最后这五个debuff各自代表的分数就是:30 210 140 60 80,总分数为n个房间分数之和(210)当我们不要哪个debuff后,就会从总分上扣除这些分数。
比如我们不要数字1,那么就会被扣去30分,最后得到的分数就是180分。
现在问题就是如何快速的实现区间加值和知道某个debuff的分数了,如果只是单纯的遍历区间一个个加,复杂度最大会到O(m^2),显然是不行的。
这时候我们就可以用线段树来解决这一问题,线段树的区间修改和单点查询的复杂度都是logn的。这样总复杂度就是O(mlogm).
AC代码
#include
using namespace std;
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
问题解析
为了方便处理,我们可以直接把所有时间改成分钟的形式,比如1小时10分,对于我们正常的时间来说,这就是70分,那么1小时10分到2小时20分,就是70分中到140分钟。
我们直接把所有的不能打电话的时间段转化成分钟形式的区间,对于样例来说:
输入
3 24 60 2
7 0 11 15
14 20 17 35
18 50 21 10
7 1
21 11
输出
No
Yes
不能打电话的时间段就为:
- 420 ~ 675
- 860 ~ 1055
- 1130 ~ 1270
然后我们再把要打电话的时间也转化成分钟,再二分查找下我们要打电话的时间有没有出现在这些时间段即可,如果出现了,就是No,反之是Yes。
AC代码
#include
using namespace std;
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
问题解析
看到数据后我们发现,一共最多只有2000个字符串,字符串的最大长度才20,而且只有一个询问而已。那就可以先想暴力了,加上这里要求的是把s变成t的最少操作数,我们可以采用BFS的方法。
初始队列中存入字符串s。
在每一步的bfs中,我们可以枚举当前字符串的每一个位置,再在每个位置枚举a到z共26个字母,看修改过后的字符串是否有在那n个字符串出现即可,如果出现了,我们不妨把它变成这一个字符串,再把这个字符串存入队列中。直到这个字符串变成t为止。
根据BFS的性质,第一个变成字符串t的一定是最少次数。
题目还要求我们输出变化的路径,那我们可以在队列中,除了存下每一步的字符串,还存下这个字符串每次变换的过程,当这个字符串变成t后,我们把他们输出就行。
初始代码(未AC):
#include
using namespace std;
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
但是提交后就能发现,我们并不能ac题目,甚至一半的测试都没过。
这是因为我们在bfs中会走很多重复的路径,比如字符串"qwq"变成"qwa"后,下一步"qwa"又变成"qwq"了,说实话,这样很蠢。
这里就要提到BFS/DFS中的灵魂操作——减枝。
我们在优先搜索的过程中会重复走很多之前走过的路,这样加大了我们的计算量,我们只要防止它再走回之前走过的路即可,这样就能减少我们的计算量。
对于这题来说,"qwa"变回"qwq"是因为"qwq"依然在我们的那n个字符串中,所以我们枚举的时候还会变回去。那么我们只要在枚举完“qwq”的所有变式后,把这个字符串从我们那n个字符串中删除即可。这样"qwa"就不会再变回"qwq"了。
AC代码
#include
using namespace std;
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
减枝后总用时只有50ms,这就是减枝的强大之处啊!
问题解析
设一个三维的转移数组f[i][j][k],表示在坐标为(i,j)的位置上,已经连续匹配了长度为k的字符串s的前缀,且之前已经成功匹配了f[i][j][k]个字符串s。
再准备一个二维数组res来存储结果,表示在坐标为(i,j)的位置上,最多能匹配出res[i][j]个字符串s。
状态转移方程:
坐标为(i,j)的字符不能匹配s[k-1]的字符,不进行转移,直接跳过;
坐标为(i,j)的字符能匹配s[k-1]的字符,则:
- 若k==1,则说明从当前开始重新匹配一个新的字符串s,f[i][j][k]=max(res[i-1][j],res[i][j-1]);
- 若k>1,f[i][j][k]=max(f[i-1][j][k-1],f[i][j-1][k-1]);如果k==len,则说明当前已经匹配完了一个字符串s,f[i][j][k]+=1;
每个位置状态转移后,更新(i,j)位置的答案res[i][j].
最后的答案就为res[n][m].
AC代码
#include
using namespace std;
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104