CF1114E
题意:
交互题。
有一个长度为 n n n 的序列 a a a,保证它从小到大排序后是个等差数列。你不知道这个序列长什么样,但你可以询问:
- ? i ?\ i ? i 表示询问 a i a_i ai 的值;
- > x >\ x > x 表示询问序列中是否存在严格大于 x x x 的数。 你可以问不超过 60 60 60 个询问。
现在,你需要求出这个等差数列的首项(也就是 a a a 中的最小值)和这个等差数列的公差(也就是等差数列中相邻两个数的差)。
2 ≤ n ≤ 1 0 6 , 0 ≤ a i ≤ 1 0 9 2\le n\le 10^6,0\le a_i\le 10^9 2≤n≤106,0≤ai≤109。
我们会用30次询问求出序列中的最大值,此时考虑如何求出公差。
显然地,我们从序列里随便选数得到若干个 m x − a i mx - a_i mx−ai ,他们的gcd必然是公差的约数。
其实到这里就很自然地会想到随机了, 30 30 30 个 d d d 的倍数的gcd等于 d d d 的概率等价于随机选 30 30 30 个自然数gcd等于 1 1 1 的概率,显然超高。
CF1746F
题意:
给出一个长度为 n n n 的数组 a a a 和以下两种操作:
- 1 i x 1\ i\ x 1 i x:将 a i a_i ai 修改为 x x x。
- 2 l r k 2\ l\ r\ k 2 l r k:询问在数组区间 [ l , r ] [l, r] [l,r] 内是否每个出现过的正整数的出现次数都是 k k k 的倍数。若是则输出
YES
,若否则输出NO
。
你会发现这个问题很怪异,询问区间出现过的数的出现次数是否为k的倍数。
显然,如果一个数的出现次数真的是 k k k 的倍数,那把这个数的权值加起来应该也是k的倍数。更具体地,如果全部数都满足是 k k k 的倍数,那区间和也应该是 k k k 的倍数。
看起来这就是个很不错的判定方法,然而随便卡一下就死了。于是我们决定将每个数随机映射一下再用这个做法。
考虑这个判定方法的错误率,若某一个数出现次数不是k的倍数,但是权值和是k的倍数(即 c n t i ∤ k , ( x ∗ c n t i ) ∣ k cnt_i \nmid k , (x*cnt_i) \mid k cnti∤k,(x∗cnti)∣k),此时可以近似认为其概率为 x ∣ k x \mid k x∣k 的概率 ,则错误率约为 1 / k 1/k 1/k 。
当 k = 1 k=1 k=1 时直接判定正确,当 k > = 2 k>=2 k>=2 时,做三十组随机即可保证错误率低于 2 − 30 2^{-30} 2−30 。
n n n 个点 m m m 条边的有向图,每条边都有激活和失活两种状态,初始时均为激活状态。四种操作:
- 失活某条边;
- 失活以某个点为终点的所有边;
- 激活某条边;
- 激活以某个点为终点的所有边。
每次操作后回答,如果只考虑激活的边,是否满足:
- 所有的点出度均为 1 1 1
- 所有的点都满足,从这个点出发,可以走到一个环中。
玩一下两个条件,发现 2 2 2 包含于 1 1 1 ,很无语。
问题变成判所有点出度均为1。
直接看题会容易歪到根号分治去,但是其实 n = 5 e 5 n = 5e5 n=5e5 。
考虑直接维护这个二四操作还是相当地困难的,然而这是一个判定性问题,且描述相对简单,如果能想到Hash的话就大功告成了。
首先有个容易维护的必要条件,即总激活边数为n。接下来可以用的Hash的方法有很多,这里讲两种比较经典的。
- Sum Hash,随机一下权值,考虑加上每一条激活边指向的点的权值,若满足题目要求,则这个值应该等于 ∑ a i \sum a_i ∑ai ,那么直接维护这个权值和就好了,因为转化为了入度的计算,二四操作也非常简单。可以多做几次Sum Hash以提高正确率(但做一次的正确率也足够高了)
- Xor Hash,会发现在满足必要条件的前提下,问题等价于所有点出度为奇数。那么转化为了一个奇偶性判定的问题,异或就可以派上用场了。我们考虑随机权值后,将偶出度的点权值异或起来,由于点权随机,可以认为存在偶出度点时,异或和不为 0 0 0,反之为 0 0 0。 于是维护偶出度的点权值异或和,只需要正确地初始化,并在每一次边的状态变化时异或一次出点权值即可。