题目链接
D题真的有点难写,,,不过写完后发现,也还行(我冒着可能万劫不复的心态研究了下tourist的代码),尽管如此,写的时候还是感觉各种不顺,还好1A了,不过肯定得重写,。这种题非常锻炼代码能力。
这场比赛如果参加的话能暴涨啊。虚拟比赛的时候很快就做完了A B C ,然后D题不会做,暴力懒得写,写了的话估计还能涨点。
A题:
题意:给你一个序列,让你挑出一个子序列,子序列的两端必须是一样的数字,然后要使得总和最大。
考虑到选的数肯定越多越好(就算是负的,但是可以去掉嘛),所以我们只需要记录下所有的pair,first表示第一次出现的位置,second表示第二次出现的位置,然后就是枚举所有的pair,去除区间内所有的负数就好了。
B题:
题意:
给你一个排列,长度是3*10^5 , 有两种操作
1:将两个位置的数互换
2:询问从x到y的连续的数在现在的序列中需要取几次,注意:每次可以取出一个子序列,但是只能是递增的。
想了一会就想出来了,要判断取几段其实可以用线段树区间合并,sum[rt] = sum[rt<<1] + sum[rt<<1|1],如果说中间的m 和m+1位置能接上去,那就sum[rt]--,
C题:
题意:给你一个数,最大有10^18,现在每次要将这个数减去数位上的某个数,然后一直减,问你最少需要几次操作能将这个数减成0
首先是小数据,10^6,看到样例我们或许就能发现什么了,每次减去数位中最大的一个数就能最快的到达0,。为什么呢?这个可以用数学归纳法证明。
现在我们有了这样一个结论,假设f[i]表示将i变成非正数的最小代价,那么如果dp[i]是非递减的,我们是不是每次只要尽可能的减小当前的数就好了呢?显然使得。
所以我们现在就是要证明f[i]是非递减的,即单调的。
贴一段CF上的评论:来自niklasb
Let f(n) be the minimum number of moves needed to reduce the number n to zero by repeated subtraction of one of its digits.
Let D(n) be the set of digits of a number n. We have the recurrence f(0) = 0, .
I will prove to you by induction that f(n) is monotonically increasing, which means that subtracting more is always better, which implies that the greedy approach is correct.
Claim:
Base case: Obviously f(1) = 1 ≥ 0 = f(0), so the claim holds for n = 1
Inductive step: Let and (so f is monotone on the set{0, ..., n - 1}). Let k be the largest digit of n. By a simple case analysis, we can see that n - 1 has a digit l ≥ k - 1. Using the monotonicity of f, we conclude
In addition, we know that . Since k = max D(n) and because we assumed the monotonicity of f on the set {0, ..., n - 1}, we know that f(n) = 1 + f(n - k). Combined with the above inequality, this gives us f(n) ≥ f(n - 1).
本来想自己写一下,无奈数学工具啥的不会用啊。。
好了,既然我们已经有了上述结论,那么对于一个数
10^6显然可以暴力,不解释。。
10^12呢,也是可以暴力的
我们将10^12分成两段10^6的数,左边是n/10^6,右边是n%10^6,所以这样子的话每次只要将右边那个10^6级别的数用O(1)时间计算出他的最小次数以及他将变成什么数(一个非正的个位数),这个时候左边的数再借位过去,只需要暴力10^6就可以了,每次都减少了10^6嘛。注意到我们在减去右边的那部分数的时候左边的数的最大值也要考虑进去,所以加个状态就ok了
cnt[i][j]表示左边的最大值为i,右边的数为j时将j变成非正数的最小次数
derc[i][j]表示同上的状态下这个数变成了什么
具体代码可以参考最下方的链接。
接下来我们来搞定10^18吧,我感觉10^18需要对状态的分析与总状态数的计算有一个清晰的逻辑,不然绝对是不会做这个题的。
10^12的那种暴力的方法其实还是没有利用到一个非常特殊的性质。每一次将后面的数变成非正数之后,借位后新的数的形式其实是a*10^p-b,a是前面数位的最后一位,所以显然这样的总状态数是及其有限的,但是数组是没法想c2那样开了,但是我们还是想知道同样的信息,比如2856987,将856987变成非正 的最小次数与变成了什么,这两个信息,然后再一步步将当前的2856987变成非正的,再返回上一层去。其实开了map映射下就好了。
D题:
题意:有一个b*b的网格,网格中有n个箭头,都是和坐标轴平行的,现在有一些询问,类似于告诉你起始点,起始方向和进行的时间,求出最终位置。
范围的话都是10W级别的,时间t是10^15级别的。
起始对于某个位置往某个方向,我们是可以知道最近的箭头是哪个箭头的,,那么我们将每个箭头都与该方向最近的箭头串联起来,是不是会形成一条条的链或者环,关键点是每一个箭头都有唯一的后继箭头,这就可以允许我们使用暴力倍增来算了,具体实现细节太过繁琐,不过确实是一道很锻炼综合能力的题目。
https://github.com/becauseofyou/Contests/tree/master/Codeforces/ABBYY_3.0_final