Codeforces Round #616 部分题解

老年选手诈尸?

A,B

咕了。

C - Prefix Enlightenment

很容易看出这个限制条件可以推出每个点最多被两个集合包含。按照套路,很容易联想到给这两个集合连一条边,表示他们的状态要相同/不同。

因为保证了有解,所以从左往右扫的时候拿并查集维护一下每个连通块的二分图情况,选较小的那一边。

如果只被一个集合覆盖,那么就相当于强制这个集合选/不选,在做并查集的时候特判一下即可。

代码咕了。

D - Coffee Varieties (hard version)

作为一名憨憨,做法当然要与正解不同。(当然常数也比正解差……)

容易想到每种咖啡在最左边的位置统计。考虑暴力,枚举两个位置\(i<j\),加入\(i\),加入\(j\),清空。这样可以过\(K=1\)的情况。

考虑每\(K\)个点(注意不是和题解一样\(\frac K 2\))分一个块。如果我们加入\(A\)块之后再加\(B\)块,那么\(B\)块中的每个点都与\(B\)块中的前缀和\(A\)块中的后缀做了比较。如果我们倒着加入\(A\)块之后再倒着加\(B\)块,那么\(B\)块中的每个点都与\(B\)块中的后缀和\(A\)块中的前缀做了比较。可以发现,此时\(B\)块中的每个点都和\(A\)块的每个点做了比较。

先不考虑\(B\)块中点会和比自己靠后的点作比较的问题,那么只要对于任意两个块都这么做一遍就行了。然而常数不太行。

我们先做正着加的情况。考虑做完两个块之后不清空,利用原有的信息。建一张图,\(i\)块向\(j\)块连边(\(i<j\)),走这一条边相当于在队列中是\(i\)块时加入\(j\)块。那么我们需要每条边都走一遍。考虑给这个图加一些额外边,使得它存在欧拉回路,这样就可以一次走完。注意到第\(i\)个块和第\(\frac n K -i+1\)个块的出度入度相匹配,可以额外连\((\frac n K -i+1,i,\frac n K -2*i+1)\)的边,于是每个点的入度出度都相等了,可以跑欧拉回路。此时用的询问次数是\(Km\)的,\(m\)大概是\(\frac {3n^2}{4K^2}\)

我们希望反着加的情况可以用类似的方法解决,但是反着加的时候\(B\)块中的每个点都与\(B\)块中的后缀做了比较,这不是我们想要的。

注意到正着加的时候我们已经把一些点给毙掉了,剩下的点在同一块中是互不相同的,于是可以倒着加的时候不加入被毙掉的点,而是加一些同块中还活着的点充数。

于是就做完了,询问次数几乎顶着上界,但是不需要清空队列。

我才不告诉你我没看懂正解呢

代码:https://codeforces.com/contest/1290/submission/73847748

E - Cartesian Tree

做这题的前置知识:定义\(maxr(l)\)表示最大的\(r\),使得\([l,r]\)是笛卡尔树中的一个区间,不存在则是\(null\)\(minl(r)\)类似。那么对于笛卡尔树中的一个区间\([l,r]\),只要不是根,那么\(maxr(l)=r\)\(minl(r)=l\)就恰好会满足一个。

回到这题,考虑把插入换成往已经确定的位置里填数,也就是允许序列中有空位。

那么\([l,r]\)的子树大小就是\([l,r]\)中的非空位置个数,也就是\(fp(r)-fp(l-1)\)

考虑把这个东西用前置知识拆开,那么答案就是\(\sum\limits_{maxr(l)\neq null} fp(maxr(l))-\sum\limits_{maxr(l)\neq null} fp(l-1)+\sum\limits_{minl(r)\neq null} fp(r)-\sum\limits_{minl(r)\neq null} fp(minl(r)-1)\),再减掉一些关于根的常数。

人脑玩一下可以发现把序列reverse之后,后面两项是和前面两项完全对称的,所以只考虑前面两项。

发现\(maxr(l)\neq null\)等价于\(p_{l-1}>p_l\),于是第二项很好做。

对于第一项,考虑如何加入一个最大值后维护\(maxr(l)\)。由于\(maxr(l)\)就是\((l,n+1]\)中第一个比\(p_{l-1}\)大的位置减一,所以加入最大值就只会给前缀取min,以及单点修改自己和后继的\(maxr\)

你发现这东西可以segment tree beats维护,而套上个\(fp\)其实也差不多,需要在外面用树状数组维护一下\(fp\),再用一个线段树维护每个\(fp\)的贡献系数。

这里的segment tree beats因为维护的信息不是像区间和那样可以pushup,而是一个全局的信息,所以我感觉\(tag\)的维护稍微麻烦一些。在我的写法里,区间取min的标记下放的时候需要区分这个标记是新的还是旧的。

代码(不保证能让人看懂):https://codeforces.com/contest/1290/submission/73857013

update:发现官方题解好像搞复杂了,实际上直接用 segment tree beats 维护每个点的 \(l_i,r_i\) 就行了。(在这里 \(l_i,r_i\) 表示左右第一个比它大的位置)

F

咕了。

posted @ 2020-03-21 11:40  p_b_p_b  阅读(257)  评论(0编辑  收藏  举报