F. Fancy Arrays
快速幂 容斥
数列个数,看起来像快速幂,问题是没有最大值可能很大,直接快速幂的话矩阵太大。
考虑容斥转化成一个矩阵大小O ( x ) O(x)O(x)的快速幂问题:至少有一个元素在[ x , x + k − 1 ] [x,x+k-1][x,x+k−1],等价于最小值小于x + k x+kx+k,且最大值大于等于x xx的数列个数,如果最大值就在[ x , x + k − 1 ] [x,x+k-1][x,x+k−1],显然满足条件,如果最大值大于等于x + k x+kx+k,那么由于相邻项跨度不超过k kk,从小于x xx的最小值到大于等于x + k x+kx+k的最大值中间,一定有一步会落在[ x , x + k − 1 ] [x,x+k-1][x,x+k−1],这类似于离散版的介值定理。
故我们要求的数列个数,就可以由容斥转化为:等价于最小值小于x + k x+kx+k的个数,减去最大值小于x xx的个数。
最小值小于x + k x+kx+k的方案数,从每一步的跨度构成的数组来看,确定了这个数组就能确定数列的形状,每一步跨度都有[ − k , k ] [-k,k][−k,k]共2 k + 1 2k+12k+1种选择,一共有( 2 k + 1 ) n (2k+1)^n(2k+1)n种形状,然后可以上下平移,对于每一个形状,让他的最小值小于x + k x+kx+k,也就是在[ 0 , x − k + 1 ] [0,x-k+1][0,x−k+1],有x + k x+kx+k种选择,总方案数( x + k ) ( 2 k + 1 ) n (x+k)(2k+1)^n(x+k)(2k+1)n
最大值大于等于x xx,也就是所有元素值域都在[ 0 , x − 1 ] [0,x-1][0,x−1],且跨度不超过d dd,x xx很小,直接矩阵快速幂d p dpdp即可
E. Darth Vader and Tree
树上dp 矩阵快速幂
自相似的树,考虑一个点的儿子,儿子为根的子树,和父亲为根的子树是一样的,可以通过这一点来d p dpdp转移,比如本题,统计到根距离x xx的点,等价于到儿子距离为x − d i x-d_ix−di的点,每个儿子都可进行这样的转移。
注意儿子虽多,但d i < = 100 d_i<=100di<=100,并不多,可以用桶统计各种d i d_idi的儿子,一次一共只有100 100100个转移。并且不同的x xx,转移都是一样的f x = ∑ f x − i f_x=\sum f_{x-i}fx=∑fx−i,只有100 100100个转移,可以考虑矩阵快速幂优化。
C. Fountains
枚举 二分
买一个金币,一个钻石,分别求不超过对应货币数的最大价值即可。
买两个金币,可以枚举一个,然后可知剩余货币,求价值不超过剩余货币的最大价值,把金币按照价值升序,然后维护前缀最大价值,再二分即可。
两个钻石同理
E. Hidden Knowledge of the Ancients
滑窗 map维护元素种类
元素种类随区间边长有单调性,滑窗即可,注意还有区间长度的限制。
C. Monocarp’s String
扫描线前缀和 dp
删去一个区间,剩余的ab相等,等价于选择一个区间a − b a-ba−b的个数为特定值。数据结构维护a − b a-ba−b个数的前缀和,枚举所有前缀,对于一个前缀,计算出,可以和他形成合法区间的另一个前缀,前缀和应该是多少,去数据结构查询这个前缀和值的最大长度,即可得到以当前前缀结尾,为右端点的最短合法区间
如果s ss包含三种字母,类似地可以分别维护a − b , b − c , c − a a-b,b-c,c-aa−b,b−c,c−a的个数,这三个值共同组成一个前缀的状态,把他们压缩成一个对象,作为m a p mapmap的查询k e y keykey,剩下照旧。
该方法可以拓展到更大的字符集。
D. Flags
带前缀和的矩阵快速幂 状压 回文
不能出现黑白红,红白黑,状态里需要记录前两个元素的颜色。
需要求很长的序列的方案数,考虑矩阵快速幂优化,状压记录前两个颜色,并根据其它规则构造转移矩阵。
求的是区间和,可转化为前缀和,求的是长度[ 1 , x ] [1,x][1,x]之间的序列的方案数,这可以在d p dpdp转移同时维护一个前缀和项,第i ii轮的前缀和项,首先继承i − 1 i-1i−1轮的前缀和,并且所有( i − 1 , i ) (i-1,i)(i−1,i)的转移,都要复制一份转移到前缀和项,相当于前缀和加上了i ii的方案数。
还要去除逆序,也就是回文串贡献一次,两个互为逆序的只贡献一次,答案是( 全部 − 回文 ) / 2 + 回文 (全部-回文)/2+回文(全部−回文)/2+回文,所以还需要求回文个数。回文只需要枚举前一半,后一半根据回文特性已经确定了,也可以套用前面的矩阵快速幂优化d p dpdp,长度除二即可
B. Pairs of Numbers
正序转倒序
状态是个数对,顺着到达目的地,不好做,考虑从目的状态出发倒着来,往往是正着会有很多分支,但倒着来路径是确定的。
比如这题正着每一步有两个分支,倒着等价于( a , b ) (a,b)(a,b)里较大的减去较小的,过程是完全确定的。接下来求步数,由于a , b a,ba,b很大,不能完全模拟。观察模拟过程,实际上就是大的不断减去小的,知道大小关系变化,熟悉数论的可以发现这就是求g c d gcdgcd辗转相减,只是g c d gcdgcd关心最终a , b a,ba,b的值,我们现在关心进行的操作次数,那么对其优化显然就是优化成辗转相除,每次较大的对较小的取模,即可直接跳过一段相同的减法操作,有解的话,最后应该是( a , b ) = ( 1 , x ) (a,b)=(1,x)(a,b)=(1,x)的形式,如果是( 0 , x ) (0,x)(0,x)则无解。
终点有多种选择,注意到( i , n ) ( n , i ) (i,n)(n,i)(i,n)(n,i)是对称的,只有枚举一边,并且i > n i>ni>n的,第一轮会先转移到一个( i , n ) , i < n (i,n),i<n(i,n),i<n,所以我们只用枚举i ∈ [ 1 , n ] i∈[1,n]i∈[1,n]就是最优的
D. Count GCD
gcd 容斥
gcd是个不断取质因子集合交集的过程,因此首先随着元素增加,gcd一定不断变小。其次增加一个元素之后,新的gcd一定是之前的一个因子。最后来考虑方案数,前一个gcd=y,当前gcd=x,那么这一次加入的元素,一定是x xx的倍数,但不是z = k x , x < z < = y z=kx,x<z<=yz=kx,x<z<=y的任何一个z zz的倍数,否则gcd会是z而不是x。
在值域内,总的x xx倍数有m / x m/xm/x,我们要减去所有至少是一个z = k x , x < z < = y z=kx,x<z<=yz=kx,x<z<=y的倍数的数字的个数,就是合法的元素个数。注意到至少是集合中一个数的倍数,这可以容斥计算。
C. Strong Password
子序列自动机
如果是子序列,也就是能匹配上,我们如何验证?就是经典的子序列自动机,我们把模式串作为自动机,s作为输入的语言,枚举s,根据当前在t上的指针位置,决定t形成的自动机能否转移,如果能转移到结尾就能匹配上。
现在问能否不匹配上,仍然考虑把t作为自动机,s作为输入串,匹配不上就是扫完s,没转移到t的终态,我们能构造t,那贪心的想,就要通过构造,让t的转移尽量慢,也就是,直到s里[ l i , r i ] [l_i,r_i][li,ri]的字符都出现过了,我们才认为t tt的i ii位置匹配上了,这样匹配时最慢的,如果这样最后仍然匹配上了,则怎么构造t tt,都不存在t tt不是s ss的子序列。
F. Points
线段树维护滑窗
考虑静态。可以枚举z zz,然后对于每个z zz,在[ z − d , z − 1 ] [z-d,z-1][z−d,z−1]里选两个不同元素作为x , y x,yx,y,假设[ z − 1 , z − d ] [z-1,z-d][z−1,z−d]元素个数为k kk,那么贡献就是C ( k , 2 ) = k ( k − 1 ) / 2 = ( k 2 − k ) / 2 C(k,2)=k(k-1)/2=(k^2-k)/2C(k,2)=k(k−1)/2=(k2−k)/2
动态增减元素的话,考虑对值域建线段树,每个位置i ii维护[ i − d , i − 1 ] [i-d,i-1][i−d,i−1]的k , k 2 k,k^2k,k2,用一个区间求和线段树,得到所有位置的k 2 , k k^2,kk2,k和,即可求出答案。
这其实是一种不太常见,但也有套路的线段树设计,也就是线段树上i ii点,不对应序列/值域的i ii点,而是对应以i ii开头/结尾的一段区间,这常见于原问题静态时,答案的求法时跑一个定长滑窗,确定了滑窗的端点即可统计答案,那么我们让线段树维护这些滑窗内的情况,即可解决动态问题。
考虑更新,加入一个数i ii,会影响[ i , i + d ] [i,i+d][i,i+d]为右端点的滑窗的k , k 2 k,k^2k,k2,也就是一个区间更新,需要懒标记,懒标记的核心就是p u s h d o w n pushdownpushdown和更新时递归到一个最小区间的更新,两部分逻辑类似,一块考虑。
也就是我们对一个区间的k kk都+ f +f+f,这个区间的k , k 2 k,k^2k,k2标记分别会如何变化?k kk的变化就是每一个位置i ii,都+ f +f+f,但前提是i ii位置有元素,所以整个区间的k kk变化是,+ = c n t ∗ f +=cnt*f+=cnt∗f,c n t cntcnt是区间内有元素的位置个数,因此区间标记需要增加一个c n t cntcnt,这很好维护,单点修区间和即。k 2 k^2k2的变化,写出增量表达式( k + f ) ( k + f ) − k 2 = ∑ ( 2 k f + f 2 ) (k+f)(k+f)-k^2=\sum (2kf+f^2)(k+f)(k+f)−k2=∑(2kf+f2),这里的求和也是对所有位置i ii来说,前提是i ii位置有元素。故等于2 f ∑ k + c n t f 2 2f\sum k+cntf^22f∑k+cntf2,用k , c n t k,cntk,cnt标记即可更新。
加入一个数,最后递归到i ii的叶子,更新这一点的c n t = k = k 2 = 1 cnt=k=k^2=1cnt=k=k2=1
删除一个数,对k , k 2 k,k^2k,k2的区间更新和上面类似,可以复用函数,f = − 1 f=-1f=−1即可。单点修该则是i ii位置c n t = k = k 2 = 0 cnt=k=k^2=0cnt=k=k2=0