继续垂死挣扎

博客写得太长看得时候会很累,于是再开一篇。顺便在这里记一下主席的ZR密码:xyt1538482243

CF590E Birthday

对于两个字符串\(S_i,S_j\),如\(S_i\)\(S_j\)的子串,那么我们就称\(S_i\leq S_j\)。不难发现\((S,\leq)\)构成了一个偏序集。我们要找到一个子集使得两两字符串不存在包含关系,就等价于找到一个子集两两不可比,即找到最大反链。

首先考虑把这个偏序关系求出来,一个简单的想法是建出AC自动机,把每一个串拿上去跑,每到一个节点就暴力跳\(fail\),这样的时间复杂度是\(O(n\sum |S_i|)\)。不难注意到跳\(fail\)的过程中跳到第一个有结束标记的节点就没有必要往上跳了,因为偏序关系具有传递性,所以那些更靠上的点一定会被其他这个点跳到。于是再建自动机的时候维护每个点往上最近的带有结束标记的点,这样最多只需要跳一次。最后用floyd求一个传递闭包即可。

根据\(\rm Dilworth\)定理,最大反链长度=最小偏序集划分。显然这个偏序关系构成了一个\(\rm DAG\),在\(\rm DAG\)上,最小偏序集划分=最小链划分。最小链划分是一个经典问题,将原图中的每个点\(i\)拆成\(i\)\(i'\),对于原图中的一条边\((x,y)\),我们建边\((x,y')\),这样就得到了一张二分图,最小链划分=原图点数-最大匹配数。于是跑一个匈牙利即可。

之后是根据最小偏序集划分求出最大反链,大概就是这样:

时间复杂度\(O(n^3+\sum_{i=1}^n|s_i|)\)代码

CF526F Pudding Monsters

一番转化发现就是求满足\(\max-\min+1=r-l+1\)的区间\([l,r]\)的数量。枚举\(r\),那么\(\max-\min+l=r\),不难发现\(\max-\min+l\)一定是最小值,于是用线段树维护区间最小值和最小值出现次数,\(\max,\min\)用单调栈维护即可。

时间复杂度\(O(n\log n)\)代码

CF1270F Awesome Substrings

\(s_i\)为序列的前缀和,一个区间\([i,j]\)是好的当且仅当\(i-j=d(s_i-s_j),d\in \mathbb{N^*}\)。移一下项可以发现即为\(i-ds_i=j-ds_j\),这样一个区间\([i,j]\)是好的条件就转化为\(g_d(i)=g_d(j)\),设\(g_d(i)=i-ds_i\)。设一个阀值\(B\),对于所有不超过\(B\)\(d\),我们求出其\(g_d(i)\)的值,设满足\(g_d(i)=x\)\(i\)\(y\)个,那么就会有\(\binom{y}{2}\)的区间是好的。这边优秀的处理即可做到\(O(nB)\)

再来考虑\(d>B\)的情况,即\(\frac{i-j}{s_i-s_j}>B\),即\(s_i-s_j<\frac{i-j}{B}\)。显然\(s_i-s_j\)不会超过\(\frac{n}{B}\)。对于每一个数我们处理其后面第一个\(1\)的位置,枚举一个点作为左端点,之后往后跳\(1\)的位置即可,当\(1\)的个数超过\(\frac{n}{B}\)的时候就停下来。这样相当于在枚举\(s_i-s_j\)的值,有了\(1\)的个数就可以直接算了。这里计算的时候还需要注意去掉\(d\leq B\)的情况,这边的复杂度是\(O(n\frac{n}{B})\)

显然根号平衡一下复杂度就是\(O(n\sqrt{n})\)

HZOI省选模拟84C 字符串

两个串\(S,T\)相似的定义是至多存在一个不相等的位置。考虑\(S=Xc_1Y,T=Xc_2Y\),显然这个时候\(S\)\(T\)相似,不难发现\(X\)\(S,T\)的最长公共前缀,\(Y\)\(S,T\)的最长公共后缀。于是\(S,T\)相似的充要条件为\({\rm LCP}(S,T)+{\rm LCS}(S,T)\geq m-1\)

不难想到\({\rm LCP},{\rm LCS}\)分别是后缀树和前缀树上\(lca\)的深度,于是把两棵树用SAM建出来。对于每一个长度为\(m\)的子串,我们求出其在两棵树上的位置。在第一棵树上做启发式合并,这样相当于确定了\({\rm LCS}\),这样就有\({\rm LCP}\geq m-1-{\rm LCS}\),就可以利用树上倍增在第二棵树上确定位置了。显然满足条件的点在一棵子树中,于是我们按dfs序维护线段树,求一下这个子树内有多少点,再去线段树上区间加。可能需要标记永久花等高明的实现方法。

复杂度时\(O(n\log^2 n)\)代码。据说有厉害的做法能做到\(O(n\log n)\)

CF875E Delivery Club

先二分一个\(mid\),之后是经典套路,设\(f_{i,j}\)表示第\(i\)时刻一个人在\(x_i\)位置,另一个人在\(j\)位置是否可行。有两种转移,一种是第一个人走到\(x_{i+1}\)位置,即\(f_{i,j}\rightarrow f_{i+1,j}\);另一种是在\(j\)位置的人移动到\(x_{i+1}\),即\(f_{i,j}\rightarrow f_{i+1,x_i}\)。由于任意时刻距离都不能超过\(mid\),所以只有当\(|j-x_i|> mid\)的时候\(f_{i,j}\)一定是\(0\)

发现这个dp的两种转移都非常简单,第一种转移就是直接继承上一层的状态,第二种转移只操作一个位置,于是考虑整体dp;我们用一个set来维护满足\(f_{i,j}=1\)\(j\),第一种转移就是直接继承前一个set,第二种转移就是单点插入。之后我们需要删除\(|j-x_{i+1}|>mid\)\(j\),这样的\(j\)一定位于set的两端,暴力删除即可。由于只插入了\(n\)次,所以删除的复杂度时均摊线性的。套上一个二分,复杂度就是\(O(n\log n\log w)\)

这个两个\(\log\)的做法看起来有点垃圾,考虑进一步优化;考虑求出每一次插入被删除的时间,这样就能知道是否有一个时刻set为空;进一步观察,并没有必要对于每一个元素都求出其被删除时间,由于只需要求是否有一个时刻被删空,所以我们只需要关心当前删除时间最晚的在哪里被删除就好了。设目前已经处理了前\(i\)个,最晚删除时间为\(t\)。对于\(i+1\),我们先用一个st表\(O(1)\)判断一下其是否在\(t\)之前就被删除了;如果被删除了,那么\(i+1\)一定不如之前某个元素优,直接跳过就好了;如果还没被删除,那么我们就往后暴力就出其在何时被删除。这样一次\(check\)的复杂度就来到了\(O(n)\);于是复杂度就是\(O(n\log w)\)代码

LGP6556 The Forest

用一下树上连通块满足\(|V|-|E|=1\)的性质,可以写一个\(O(n^2)\)的暴力;反过来考虑一条红色树上的边\((u,v)\)在蓝色树上的哪些路径中出现过,发现一条路径只需包含\((u,v)\)即可,这个条件可以用dfs序写成矩阵的形式,于是考虑用数据结构来维护。

外面先套一个点分治,之后dfs当前分治块;扫到一个点\(u\)的时候我们遍历一下与其在红色树上相邻的点\(v\),如果\(v\)不在当前分治块里,那么分治块中不可能存在一条路径包含\((u,v)\);否则的话我们分情况讨论一下。

  • \(v\)\(u\)的祖先节点,那么从\(u\)出发的任何一条经过分治重心的路径都包含\((u,v)\)这条路径,于是令整体减一。

  • \(v\)在其他子树中,那么任取\(v\)子树中的点\(x\)\((x,u)\)都包含\((u,v)\)这条路径,于是令\(v\)子树中的点都减一;其实就是在dfs序上区间减一。

用线段树来维护区间最小值和区间最小值个数即可,复杂度是\(O(n\log^2 n)\)代码

CF1278F Cards

其实就是求

\[\sum_{i=0}^n\binom{n}{i}p^i(1-p)^{n-i}i^k \]

因为\(k\)不大,所以大家都知道\(i^k\)需要用斯特林数展开一波,于是展开来大概是这个样子

\[\sum_{j=1}^k\begin{Bmatrix}k\\j\end{Bmatrix}j!\sum_{i=1}^n\binom{n}{i}\binom{i}{j}p^i(1-p)^{n-i} \]

\(\binom{n}{i}\binom{i}{j}\)展开成\(\frac{1}{j!}\frac{n!}{(i-j)!(n-i)!}\),想办法把后面配成组合数,因为\(n!=(n-j)!n^{\underline j}\),于是可以写成\(\frac{n^{\underline j}}{j!}\binom{n-j}{i-j}\),提出来之后大概长这样:

\[\sum_{j=1}^k\begin{Bmatrix}k\\j\end{Bmatrix}n^{\underline j}\sum_{i=1}^n\binom{n-j}{i-j}p^{i}(1-p)^{n-i} \]

不难想到改写一下里面那个\(\sum\)的上下角标,于是\(\sum_{i=1}^n\binom{n-j}{i-j}p^{i}(1-p)^{n-i}=\sum_{j=0}^{n-j}\binom{n-j}{i}p^{i+j}(1-p)^{n-j-i}\),提出一个\(p^i\)后用一下二项式定理,于是后面就全没了。最后答案就是

\[\sum_{j=1}^k\begin{Bmatrix}k\\j\end{Bmatrix}n^{\underline j}p^j \]

NTT即可做到\(O(k\log k)\)代码。还有一个\(O(k)\)做法是把第二类斯特林数用容斥的形式展开后,再递推一个柿子。

ZR2020寒假省选集训Day3 A

考虑到点分树的形态和点分的方案是一一对应的关系,于是我们转为统计点分树的形态数。考虑现在有一棵点分树,断掉原树中的一条边\((u,v)\)后会对点分树有什么影响;断掉一条边后原树形成了两个联通块,把其中一个联通块的点称为黑点,剩下的称为白点;可以发现对于每一个黑点(白点),找到其点分树祖先中最近的黑点(白点)作为其新的父亲,这样各有白点和黑点各有一个找不到新父亲,可以将其直接断开;这样就得到了两个新的点分树,这就是原树断开一条边后得到两个联通块的点分树。这就是点分树的分裂。

再来考虑连接\((u,v)\)这条边会有什么影响,首先我们要从\(u\)\(v\)所在的两棵点分树中选一个当根,根在原点分树中的儿子仍作为其儿子,之后用剩下的点组成一棵点分树来做根的儿子;不难发现本质上就是\(u\)到根路径上的点和\(v\)到根路径上的点按照深度归并了起来,这样就得到的点分树就是连接\((u,v)\)后新树的点分树了。于是我们设\(dp_{x,i}\)表示\(x\)在点分树中深度为\(i\)的方案数,把原树中的每一条边连上去即可,转移的话就是枚举原来\(x\)上面有多少点,即\(dp'_{x,i}=\sum_{j=1}^i\sum_{k=i-j}^n dp_{x,j}dp_{v,k}\binom{i-1}{j-1}\),预处理\(dp_v\)的后缀和就能做到\(O(n^2)\)代码

ZR2020三月省选集训Day2 B

我们发现操作一个点只会影响其祖先中的点,于是可以将这个问题视为翻硬币博弈。在翻硬币博弈中,局面的\(SG\)值等于所有可以被操作的硬币单独存在时的\(SG\)值的异或和,于是我们对于每一个白色节点\((i,j)\)求一下\(SG(i,j)\)之后异或起来即可。

经过打表可以发现,\(SG(i,j)={\rm lowbit}(\max(i,j))\)。由于白色节点以矩形的形式给出,数量较多,同时注意到\(n\leq 10^5\),所以可以考虑用类似扫描线求矩形面积并的方式来做。把一个矩形\((x_l,x_r,y_l,y_r)\)拆成\((x_l,y_l,y_r,1)\)\((x_r+1,y_l,y_r,-1)\),表示在\(x_l\)时刻插入一条线段\([y_l,y_r]\),在\([x_r+1]\)时刻删除线段\([y_l,y_r]\)。对于时刻\(i\),我们求一下\(\leq i\)的点有多少个,这些点对答案的贡献都是\({\rm lowbit}(i)\);在统计一下大于\(i\)且被覆盖了的点的\(\rm lowbit\)的异或和。由于需要快速求出一个区间的\(\rm lowbit\)异或和,所以我们直接将线段树的值域设为\([0,2^{30})\),这样线段树上每一段区间的长度都是\(2\)的整数次幂,就能快速求出这段区间的\(\rm lowbit\)异或和。

之后在线段树上维护一下最小值和最小值的\(\rm lowbit\)异或和即可,复杂度是\(O(n+k\log m)\)代码

ZR2020三月省选集训Day2 C

将这棵有根树视为外向树,所求即所有拓扑序的顺序对。考虑两个点\(i,j\)\(i<j\),我们求一下在多少种拓扑序中,点\(i\)出现的位置比\(j\)靠前。首先如果\(i\)\(j\)的祖先,那么在所有拓扑序中,\(i\)都会比\(j\)靠前。于是\(O(n)\)求一下树的拓扑序个数就可以直接算了。

对于\(i,j\)不存在祖先关系的情况,先预处理一个\(dp_{i,j,k}\)表示点\(i\)在子树\(j\)中拓扑序为\(k\)的方案数,转移即合并两棵子树的时候枚举一下有多少个点放在\(i\)前面,比如在点\(i\)前面放\(t\)个,那么就需要\(dp_{i,j,k}\)乘上\(\binom{k+t-1}{t}\binom{sz_v+sz_j-k-t}{sz_v-t}\),即在前面插入\(t\)个后\(i\)的排名变为了\(k+t\),那么就从前\(k+t-1\)个位置里选出那\(t\)个要插入的点放在哪里,剩下的\(sz_v+sz_j-k-t\)个位置里再插入剩下的\(sz_v-t\)个点的位置,还需要乘上子树\(v\)的拓扑序个数\(g_v\),转移到\(dp_{i,j,k+t}\)。转移复杂度同树上背包,对\(n\)个点都求一遍复杂度为\(O(n^3)\)

之后我们钦定两个不是祖先关系的点\(i,j\),使得他们在\(lca\)处合并,使得编号较小的点\(i\)出现在前面。合并的方式和上面差不多,也是枚举在\(i\)前面插入多少个数,乘上几个组合数,求出这个方案数再正常求一遍拓扑序即可。由于合并的时候两个子树可能会很大,所以这个做法是\(O(n^4)\)的。进一步观察发现并不需要枚举\(j\),我们多个\(j\)的方案数加起来一起算即可,这样复杂度就变成了\(O(n^3)\)代码

ZR2020三月省选集训Day2 A

一个经典trick,前缀\(\gcd\)变化次数是\(O(\log w)\)级别的,于是用一个单调栈可以在\(O(n\log w)\)的时间内求出一堆四元组\((l,r,x,v)\),表示\(\forall i\in [l,r]\),区间\([i,x]\)\(\gcd\)\(v\)。这些四元组能表示所有区间的\(gcd\)

考虑正难则反,改为对每个点求有多少种方案不包含它,之后拿总方案数一减即可。考虑使得所有选出的区间的\(\gcd\)\(w\),设\(pre_i\)表示从\([1,i]\)中选出若干\(\gcd\)\(w\)的区间的方案数,\(beh_i\)表示从\([i,n]\)选出若干\(\gcd\)\(w\)区间的方案数。那么对于不包含点\(i\)的方案数就是\((pre_{i-1}+1)\times (beh_{i+1}+1)-1\)

考虑求出\(pre\),把所有\(v=w\)的四元组拿出来,如果不存在一个四元组\((l,r,x,v)\)满足\(x=i\),那么\(pre_i=pre_{i-1}\);否则选一段以\(i\)结尾的区间,那么\(pre_i=pre_{i-1}+\sum_{j=l}^r 1+pre_{j-1}\)。第一类转移可以直接使用线段树来区间覆盖,第二类转移求一个区间和即可。于是复杂度是和四元组个数相关的。同理,\(beh\)也可以这样求出。

相同的\(pre\)\(beh\)会构成一段段区间,区间个数即为四元组个数;于是直接将区间排序后区间覆盖,用差分实现区间加即可。复杂度是\(O(n\log n\log w)\)代码

HZOI省选模拟89A 石子

经过打表可以发现\(SG(x)=\log_2 \rm lowbit(x)\);证明的话考虑证明\(SG(x)=SG(xq)\)\(q\)为一奇数,大概就是考虑\(SG(x-d_1),SG(x-d_2)\dots SG(x-d_m)\)\(SG(q(x-d_1)),SG(q(x-d_2))\dots SG(q(x-d_m))\)一一对应,之后归纳下去就好了;

之后设\(tax_i\)表示\(SG(x)=i\)\(x\)有多少个,显然\(tax_i=\lceil\frac{\lfloor\frac{n}{2^i}\rfloor}{2}\rceil\),这玩意大概需要写一个高精度;现在将问题转化成了选\(m\)个数,第\(i\)中数有\(tax_i\)种选法,问异或和不为\(0\)的方案数,这直接把\(tax_i\)给fwt掉后对位乘\(m\)次,即每个数取一个\(m\)次方,再ifwt回来就好了;时间复杂度\(O(\log n\log m+\log n\log \log n)\)

HZOI省选模拟82C 绝对伏特加

题意:有\(n\)个整形随机变量,均为\([1,k]\)等概率随机,设\(a_i\)表示\(i\)出现的次数,求\(\prod_{i=1}^L a_i^F\)的期望,对\(2003\)取模。

考虑\(a_i^F\)的组合意义,我们把\(i\)出现的位置列出来,分别为\(A_1,A_2\dots A_{a_i}\)\(a_i^F\)表示从这\(a_i\)个位置中选\(F\)个出来,可以有重复;而\(\prod_{i=1}^L a_i^F\)就是从对于每个\(a_i\)都选\(F\)个位置,由于一个位置的取值唯一,所以不同的\(a_i\)选出的位置不同。

\(b_i\)表示\(i\)选出来的\(F\)个出现位置构成集合的大小,即去重后的大小;考虑先给每个\(i\)选好出现的位置,那么方案数就是\(\binom{n}{b_1}\times \binom{n-b_1}{b_2}\times \dots \times \binom{n-\sum_{i=1}^{L-1}b_i}{b_L}\),,将这个组合数拆开约分后发现其实就是\(\frac{\binom{n}{\sum_{i=1}^L b}(\sum_{i=1}^L b)!}{\prod_{i=1}^L b_i!}\),注意到这个分母是一堆阶乘,于是不难想到需要搞一个\(\rm EGF\)

设生成函数为\(F(x)=\sum_{i=1}^F D(i,F)\frac{x^i}{i!k^i}\)\(D(i,F)\)表示一个长度为\(F\)的序列,\(i\)种数都出现至少一次的方案数,选出的这\(i\)位置都要填上特定的数于是概率是\(\frac{1}{k^i}\)。不难发现\(D(i,F)=[F](e-1)^iF!\),于是我们可以在\(O(F^3)\)的时间内求出所有数的\(D(i,F)\),这样把\(L\)\(F(x)\)乘起来就好了。注意到最后要乘一个\((\sum_{i=1}^L b)!\),当\(\sum_{i=1}^L b_i \geq mod\)的时候,显然有\((\sum_{i=1}^L b)!=0\);于是再做多项式乘法的时候对\(x^{mod}\)取模即可,这样做的复杂度是\(O(F^3+mod^2\log m)\)

进一步,我们发现\(D(i,x)\)可以直接使用第二类斯特林数写成\(S(F,i)i!\),于是\(O(F^2+mod^2\log m)\)即可。代码

posted @ 2020-05-07 20:30  asuldb  阅读(29)  评论(0编辑  收藏  举报