Codeforces Round #588 (Div. 1)


Contest Page

因为一些特殊的原因所以更得不是很及时……

A

sol 不难发现当某个人diss其他所有人的时候就一定要被删掉。

维护一下每个人会diss多少个人,当diss的人数等于剩余人数$-1$的时候放队列里,每一次取队头更新其他人diss的人数。

code

B

sol 一个结论:对于序列$a_1,a_2,...,a_n$,其前缀$gcd$数量不超过$log_2a_i$种。证明考虑从前往后计算前缀$gcd$,那么从第$i-1$个$gcd$到第$i$个$gcd$,数值要么不变要么至少除以$2$。

所以dfs维护一下每个点到根的路径上的每种$gcd$和其范围就可以算答案了。

code

C

sol 首先算答案可以枚举三元组$(i,j,k)$的$j$就可以用入度数量乘出度数量更新答案。实现的话似乎直接建反图暴力更新是对的,但是我的写法比较鬼畜:

按照度数根号分治,对于度数$\leq \sqrt{n}$的暴力,对于度数$\geq \sqrt{n}$和度数$\geq \sqrt{n}$之间的边暴力,对于度数$\geq \sqrt{n}$和度数$\leq \sqrt{n}$之间的边,在度数$\geq \sqrt{n}$的点用一个队列记录下有多少个$\leq \sqrt{n}$的点指向它,每一次修改的时候把队列里的元素清掉。这个复杂度显然是$O(q\sqrt{n})$的。

分析一下直接建反图的复杂度:设每个点的势能函数为连到它的点的数量,对于度数$\leq \sqrt{n}$的点它们的势能函数值不超过$\sqrt{n}$所以每一次操作的复杂度不超过$\sqrt{n}$;对于度数$\geq \sqrt{n}$的点每一次操作不会使得这些点的势能函数之和增加超过$\sqrt{n}$,所以是均摊$\sqrt{n}$的。所以复杂度就是$O(q\sqrt{n})$。

code

D

sol 先写标算做法。先把每个排列康托展开压成一个数,然后设$f_{i,j}$表示排列$i$经过置换$j$换一次之后会到哪里。这个可以$O((k!)^2k^2)$做。

枚举右端点$r$对于所有左端点计算答案。考虑如果我们可以通过已经拥有的置换从$i$变为$j$就连边$(i,j)$,那么现在相当于要求对于所有左端点将所有这样的边加进去之后$0$所在的联通块大小。

然后根据群论拉格朗日定理,我们可以知道对于区间$(1,r)$来说有用的置换最多只有$log_2k!$种,于是记录一下对于每一个右端点来说每一个置换最后出现在哪里,每一次取一个在图中$0$走不到的出现在最右的排列加进置换群中,用bfs更新一下可以到达的排列然后贡献答案。复杂度应该是$O((k!)^2k^2+nk!(k+log_2k!))$的……

然而上面的做法非常鬼畜,我们考虑一个清新一点的做法:

我们认为边$(i,j)$的边权是能将排列$i$变为排列$j$的置换的最晚出现时间,那么有用的边显然只会在这个图的最大生成树上。我们每一次移动右端点相当于加入$k!$条边,用Kruskal维护一下,然后在最大生成树上求出每个点到$0$的瓶颈路长度,那么当左端点小于等于这个长度的时候这个排列就可以出现。所以就用所有瓶颈路长度和贡献答案就行了。这个的复杂度应该是$O(nk!log_2k!)$的?反正我没写。

code(标算)

E1

sol Meet-in-the-middle。设$f_i$表示右端$R_1,R_2,R_3$三个点能够匹配左边的三元组的集合为$i$的概率,其中$i$是一个$\binom{6}{3} = 20$位二进制数,第$0$位表示匹配$(L_1,L_2,L_3)$,第$1$位表示匹配$(L_1,L_2,L_4)$,以此类推。这个东西可以$O(2^{\frac{n^2}{2}}\binom{n}{3})$爆搜。

再设$g_i$表示右端$R_4,R_5,R_6$能够匹配左边的三元组的补集集合为$i$的概率,也就是说这里表示第$0$位表示匹配$(L_4,L_5,L_6)$,第$1$位表示匹配$(L_3,L_5,L_6)$,以此类推。

那么我们要求的就是$\sum\limits_{i} f_i \sum\limits_{i \land j = 0} g_j$,后面那部分东西可以高维前缀和计算。

代码没写

E2

sol 设一个$128$位数$k$表示当前有哪些$L$的子集能与当前的$R$匹配。因为Hall定理等各种因素,能够满足二分图匹配条件的$k$的数量只有$S=64184$种,我们考虑把这些状态搜出来。我们用bitset存$k$,每一次考虑在当前的$R$上新加入一个点,枚举$R$的连边情况然后更新$k$的取值。这一部分的复杂度可以做到$O(S\frac{n2^{2n}}{w})$,稍微大一点也没关系反正跑得过。

在搜索的过程中用unordered_map把状态哈希起来并记录右边新加入的点的连边情况分别是什么时会转移到哪个状态。然后DP:设$f_{i,j}$表示当前决定了$R$中的前$i$个点的连边,当前匹配状态为$j$的概率。转移暴力枚举当前点转移。

复杂度$O(S\frac{n2^{2n}}{w} + nS2^n)$。

code

F

sol 设$x_i$表示从$i$到$i+1$的coin数,如果$x_i < 0$表示有$|x_i|$coin从$i+1$到$i$,$x_0 = x_n$。当$x_1,x_2,...,x_n$确定了之后可以很容易构造出步数为$\sum\limits_{i=1}^n |x_i|$的方案(而且这是下界),所以题目就变成了在满足$\forall i , a_i - x_i + x_{i-1} \in [l_i,r_i]$的情况下最小化$\sum\limits_{i=1}^n |x_i|$。

枚举$x_1$然后DP:设$f_{i,j}$表示决定了$x_1,x_2,...,x_i,x_i=j$时$\sum\limits_{k=1}^i |x_k|$的最小值。那么$f_{i,j}$就由$f_{i-1,k} , k \in[a_i + j - r_i , a_i + j - l_i]$转移而来。初始值$f_{1,x_1}=|x_1| , f_{1,others} = inf$。

把$f_{i,j}$视作以$j$为自变量的离散函数$f_i(x)$,那么从$f_{i-1}(x)$到$f_{i}(x)$需要做这些事情:1、将$f_{i-1}(x)$往左平移$(a_i-r_i)$;2、令$g(x) = \min\limits_{i=x}^{x+(r_i-l_i)} f(i)$;3、$f_{i+1}(x) = g(x) + |x|$。

如果$f_{i-1}(x)$是凸包那么$f_i(x)$也是凸包,而$f_1$的定义域只有$x_1$,所以$f_i(x)$就是凸包。对于凸包一个实用的维护方法是维护拐点,一个拐点表示右边线段比左边线段斜率大$1$,这样就可以描述整个凸包了。这样维护可能会导致堆中有多个相同元素,这没有什么关系。

$1$操作打个横坐标全局标记就行,$2,3$操作有点难搞。分析一下$2$操作的实质,假设最小值在$p$点处取到,那么相当于在$[p-t,p]$插入一条斜率为$0$的线段,然后把左边的凸包左移$t$。而操作$3$相当于位置>0的所有线段的斜率增加$1$,<0的所有线段斜率减少$1$。

要做一个区间$x$坐标增减、区间斜率增减、动态插入拐点、求全局最小值的问题。这个显然可以Treap解决,但是一种更优秀的做法是用两个堆分别维护右侧线段斜率>$0$和$\leq0$的所有拐点,1操作全局标记,2操作在$\leq 0$对应的堆打标记,3操作按情况,在两个堆中插入拐点、维护两个堆之间拐点的变化情况。因为每一次3操作斜率变化至多1所以至多有一个拐点在两个堆之间移动。

还有一个棘手的问题是初始状态。我们可以认为初始状态是左边有$N+5$个$x_1$位置的拐点,右边有$N+5$个$x_1$位置的拐点。这相当于$x_1$左右的射线斜率都是$N+5$,每一次操作至多修改$1$的斜率,所以这些直线一定取不到最优值。

我们可以对于一个确定的$x_1$在$O(nlogn)$时间内求出其答案了,我们现在需要解决所有$x_1$的答案的最小值。接下来是魔法时间:

1、当$x_1$为实数时最优解不变。可以把这个问题变为一个上下界网络流问题,类似于糖果传递。然后根据一个我也不知道怎么证明的定理:容量和费用都是整数的费用流一定存在一个最优解满足所有边流量都是整数,那么$x_1$为实数时最优解不变。这个引理帮助我们将$x_1$的范围由整数域拓展为实数域。

2、设有两组可行的$(x_1,x_2,...,x_n)$的取值为:$(p_1,p_2,...,p_n),(q_1,q_2,...,q_n)$,那么$(c_1=\frac{p_1+q_1}{2} , c_2=\frac{p_2+q_2}{2} , ..., c_n=\frac{p_n+q_n}{2})$也是一组可行的$(x_1,x_2,...,x_n)$。证明考虑:$a_i-p_i+p_{i-1} \in [l_i,r_i] , a_i - q_i + q_{i-1} \in [l_i,r_i]$,而$a_i - c_i + c_{i-1} = \frac{2a_i - (p_i+q_i) + (p_{i-1}+q_{i-1})}{2} = \frac{1}{2}(a_i-p_i+p_{i-1}+a_i - q_i + q_{i-1})$,两个数同时在一个区间内,那么它们的平均数也显然在这个区间内。

3、设$f(x)$表示$x_1=x$时的DP最优解,根据2的推导,如果$x_1=p_1$时$(p_1,p_2,...,p_n)$取到最优,$x_1=q_1$时$(q_1,q_2,...,q_n)$取到最优,那么$(r_1,r_2,...,r_n)$是一组可行解,它不一定最优,但是因为$|r_i| = \frac{|p_i+q_i|}{2} \leq \frac{|p_i| + |q_i|}{2}$,也就是说一定会有$\sum |r_i| \leq \frac{\sum |p_i| + \sum |q_i|}{2}$,即$f(\frac{p_1+q_1}{2}) \leq \frac{f(p_1) + f(q_1)}{2}$。根据琴生不等式,函数$f$就是一个一阶导数单调递增的凸函数,在其上有极小值。

于是我们只需要用三分法找到$f(x)$在整数域上的极小值就可以了。复杂度$O(nlognlog \sum a_i)$。

code