A
注意到友善的数据范围,直接模拟即可。
B
分类讨论,如果当前的比较矮,再比较a[i] + k 与a[i + 1]的关系进而采取不同的措施;如果当前的比较高,贪心地把当前的方块拿走直至恰好可以跳过去,当然,在这种情况最多拿的方块应该不超过a[i],从而还需要进行一个min的比较。
C
列出一堵大墙的成立关系式后可以发现:记d = gcd(n, m), n1 = n / d, m1 = m /d,那么inner的每n1个区域与outer的每m1个区域为互相可达。从而我们只需要把输入的数据根据n1、m1进行划分即可。
D
n的范围为2e3所以暴力n3会TLE,需要一种优化办法才行。比赛的时候我想不到该怎么搞,看了TOP1的D题代码后才发现这题不过是个经典的二维前缀和而已。我们先枚举每一行,双指针扫描边界的连续白格,最后可以得到中间杂色方块的长度。根据这个长度与k的大小情况和目前边的下标可以找出一个贡献矩形——在这个矩形中的erase都是可以将这行全部变成white line的,那么据此更新二维的差分数组即可。同样的,我们再枚举每一列,进行类似的操作,更新差分数组。全部枚举完毕后求出每个点的二位前缀和即可得到最终答案。
附上AC代码 http://codeforces.com/contest/1200/submission/58651693
E
附上当时对kmp算法尚且粗浅的理解 19XDU校赛现场赛_H
然后再谈谈现在也还很粗浅的一些理解:
1.一个是next数组构造的合理性问题考虑:为了清晰的说明这个问题,我们先从暴力求解的办法看起。暴力的办法是:如果发生不匹配,主串的指针前进1,然后模式串归0重来。但是next数组的存在可以让我们快速找到主串指针以及模式串指针可以直接前进的位置,这是因为,与模式串前缀相同的模式串后缀也是主串的后缀,那么主串如果在之前存在一个可疑的位置,那么该位置到刚刚匹配失效的地方也就形成一个后缀,这个后缀应该是与模式串的前缀相同的,然而我们的next数组已经保存了最大的相同前缀后缀,所以在此之前,也就不可能存在可疑的位置了。
2.next数组构造的一些理解。
public static int[] getNext(String ps) {
char[] p = ps.toCharArray();
int[] next = new int[p.length];
next[0] = -1;
int j = 0, k = -1;
while (j < p.length - 1) {
if (k == -1 || p[j] == p[k]) {
next[++j] = ++k;
} else {
k = next[k];
}
}
return next;
}
我们着重考虑while循环中间的if语句。可以这样子考虑,if语句执行的操作是一个终点,要么没有找到(返回k == -1),要么找到了一个相同的值(p[j] == p[k]),这个时候就应该更新next数组,同时j前进1。else语句执行的是一个寻找的过程:目前j给定,k停留的位置是与j长度下最长相同前缀的后一位,并且发生了p[j] != p[k]的情况,言下之意也就是这种比较长的前后缀不能成立,我们便考虑短一点的前后缀有没有可能符合条件,也就是else执行的k = next[k],k会跳到更短的前后缀的后一位,检查是否符合条件,如是反复直至产生结果。
复习结束,回头看E题。找了rank靠前的好多E题d代码,纯写字符串算法的看不懂在干啥啊…还看到一个暴力hash的也是nb…最后惊奇的发现cf每场比赛都会有题解(editorial)…!
读完后发现其实只是个kmp就行了,暂时 不需要再去dfs学习拓展kmp了。。
其实只需要把合并串和新串做一个新的构造,再Get the KMP failure function就可以了。值得注意的是,合并串和新串的长度需要分类讨论。
根据这个思路做其实已经很显然了,但是我在实际写完提交还是wa、tle了很多次…
有几点值得注意:
1.要是想要对某个string增长,效率更高的办法是 string += new_str,如果用string = string + new_str会造成很多的时间空间浪费。
csdn上有一个类似的分析 字符串String的+和+=及循环操作String的原理
2.这道题不能用朴素的next数组求解办法,需要结合题意进行一个变形。我们看这样一组字符串(test15):101, 010。如果按照常规的next数组办法,next[6] = 4,但是这样子已经超过了原字符串的长度,我们无从得知仅仅比较单个字符串的最长相同前后缀。而另一方面,我们忽视了一个隐藏的信息:构造串C其实是两个等长的字符串拼接而成(当然了,这取决于C是如何构造而成的(题解的构造办法我不喜欢,我的构造是用两个等长的进行拼接)),所以我们在终止条件再加一个判断语句:
if(i >= (d + 1) / 2) i = nex[i],将超出范围的i指针回退到之前串的某个位置再进行搜寻即可。
3.如果使用auto来表示string类的长度,注意在求nex的过程中不可以用负数跟它进行比较!!!在以前看C++primer的时候有提到:虽然我们不是很清楚string类的长度是什么数据类型,但是它一定是一个非负的、可以存得下任何长度的类型。所以如果我们用负数进行比较的话,这个负数会转化成一个超大的正数!