• 退役前的做题记录1.0


    退役前的做题记录1.0

    CF891E Lust

    我们可以考虑差分算出贡献,那么一次操作的贡献其实就是(prod a_i-prod a_i')
    令最后每个数被减了(b_i)次,那么对于一个减的方案,贡献就是(prod a_i-prod(a_i-b_i))
    那么现在的问题就是怎么算出减(i)次的贡献,因为两个数依次减去(1)的方案数是个可重排列,所以我们可以构造指数型生成函数

    [A_k(x)=sum_{i=0}^{infty} (a_k-i)frac{x^i}{i!}=e^x(a_k-x) ]

    那么答案就是

    [frac{k!}{n^k}[x^k]prod_i A_i(x)=frac{k!}{n^k}[x^k]e^{nx}prod_i(a_i-x) ]

    令后面那堆东西的乘积(F(x)=sum_{i=0}^{infty} f_ix^i)
    那么最终答案为(sum_{i=0}^nfrac{k^{underline{i}}}{n^i}f_i)
    代码

    CF938F Erasing Substrings

    有一个十分直观的想法,设(f_{i,s})表示目前在原串的第(i)位,删除的集合为(s)的最小字典序字符串,那么这样子是(O(n^3log n))的。

    考虑换个状态,设(f_{i,s})表示确定了删除之后的串的前(i)位,使用集合为(s)的方案数。那么如果两个状态(s,s')不存在包含关系,且有(f_{i,s}<f_{i,s'}),那么(s')这个状态就不行。

    因为知道了(i,s)可以确定下一个保留的字符是什么,取可以最小的转移,而如果一个状态有一个子集可以的话这个状态还是可以,高维前缀和优化下就好了,具体细节详见代码。

    代码

    AGC010F Tree Game

    想一下怎么赢下来一局,如果是两个点那么只能在多的那个点取胜,如果一个点四面八方都是比他权值大的点这个点肯定是个必败的点,否则就会往一个权值比他小的点跑。
    而每跑一步先后手会转换一下,设(f_x)表示先手在(x)能否取胜,那么可以从相邻的权值比他小的转移上来,如果有一个这样的点(v)满足(f_v=0)那么(f_x)就为(1),否则是(0)
    对每个根跑一遍复杂度(O(n^2))
    代码

    AGC033D Complexity

    最暴力的算法就是设(f[i][j][k][l])表示左上角为((i,j)),右下角为((k,l))的答案,显然复杂度爆炸,考虑优化。
    注意到答案最多为(log (N imes M)),那么我们可以考虑把答案给计入状态同时删去我们原状态中的一维。
    那么可以得出一个新的状态:(f[s][i][j][k])表示目前我们答案为(s),左上、右上角分别为((i,j),(i,k)),往下最多能延伸多少长度。
    横着切很好转移,就是一块接着上一块切了的切,即(f[s][f[s][i][j][k]][j][k] ightarrow f[s+1][i][j][k])
    考虑竖着切一刀,因为可延伸的长度就相当于左右两边取(min)而且左右两边的(f)显然单调,可以考虑二分左右原本的大小关系发生变化求出,复杂度(O(n^3log^2n))
    或者可以另设状态(g[s][i][j][k])表示左上、左下分别为((j,i),(k,i))向右延伸的最大长度是多少,那么这个转移竖着切是很好处理的,发现这样的话与(f)的转移正好互补,交替转移(f,g)即可转移,复杂度(O(n^3log n))

    代码

    CF724E Goods transportation

    考虑建图跑最大流,想办法怎么优化这个最大流。

    想到到最大流(=)最小割,可以设(f[i][j])表示目前在第(i)个点,(1-i)(j)个点和(S)连通,转移很好想,这里不再赘述,注意要滚动数组优化,复杂度(O(n^2))

    代码

    [LG3537][POI2012]SZA-Cloakroom

    直接暴力背包坑定布星,想办法怎么优化这个东西。

    将所有物品以及询问按照出现时间的右端点排序,每次按时间从小到大处理物品及询问,那么可以消除右端点的影响。设(f_i)表示(sum c=i)(min b)最大是多少,然后背包转移即可。

    代码

    [LG4229]某位歌姬的故事

    首先将所有(l,r)离散化,这样所有的位置就变为(O(Q))级别的。
    对于每个位置,我们将所有覆盖它的区间的权值取个(min),设为(mn_i),那么对于一个位置(i),只有(mn_i)这个权值是有效的。
    对于每个(m),我们可以把所有(mn_i=m)的位置拿出来,对于所有(m)分开(dp)
    对于每个右端点(i),我们记个(lim_i=max_i [r==i]l),设(f_{i,j})表示目前(dp)到位置(i),上一个取(m)的值在哪里的方案数。
    转移分为当前选(m)与不选(m)两种:
    如果选(m)就是所有(f_{i-1,j},j<i)转移到(f_{i,i})
    如果不选,就是乘上方案数再把所有(f_{i,j},j<lim_i)全部设为(0)
    最后把每个颜色的方案数乘起来就好了,复杂度(O(Q^2))

    代码

    CF1327F AND Segments

    和上题很像的一个题。
    显然对于每个二进制位拆开算然后方案数相乘,假设(X_i)的第(p)位是(x_i)
    同样的可以对于每个位置求个(lim_i),表示(max_i [x_i==0]l_i),以及设出(f_{i,j})表示目前在(i),上一个(0)的位置。
    而对于每个位置,可以差分求出这个位置是否一定要是(1)
    碰到一个必须是(1)的位置显然是(forall j, f_{i,j}=f_{i-1,j})
    如果碰到一个是(0)的位置的话,转移就是将(f_{i,i})设为(f)的前缀和,(f_{i,j}=f_{i-1,j},lim_ileq j<i)(f_{i,j}=0, ext{otherwise})
    可以数据结构优化(dp),也可以记个前缀和数组(g)做到对于每一位(O(n))

    代码

    CF1286D LCC

    第一次发生碰撞坑定是在在数轴上相邻的两个栗子间发生的。
    预处理出所有的碰撞事件,将他们按照时间排序,依次考虑每一个时间作为第一次碰撞事件的概率。
    记事件(i)作为第一次碰撞发生的概率为(Pr(i)),不发生的概率为(Pr'(i),)那么(Pr(i)=Pr'(i-1)-Pr'(i))
    现在的问题就是求出所有(Pr'(i))
    (f_{i,0/1,0/1})表示第(i)个栗子往右/左跑,上一个栗子往右/左跑的概率的和,每次( ext{ban})掉一个事件就是( ext{ban})掉相邻两个栗子分别往哪边跑,也就是( ext{ban})掉一种转移,考虑将状态压成一个矩阵,发现可以满足矩阵乘法,用线段树维护矩阵乘积即可。
    有一些实现细节详见代码。

    代码

    [十二省联考2019]皮配

    先处理一个考虑一个最暴力的(dp),就是设(f_{i,j,k})表示目前到第(i)个城市,有(j)人选蓝队,有(k)人选鸭系的方案数,再对于每个城市求出(g,i)表示该城市选(i)个鸭系的方案数即可转移。
    再考虑(k=0)的情况:可以分别求出(g_i)表示选(i)个蓝队的方案数,(f_i)表示选(i)个鸭系的方案数,然后因为不存在限制这两维互不影响,答案就是(sum_{i=S-C1}^{C0}f_isum_{j=S-D1}^{D0}g_j),其中(S)为总人数。
    注意到(kleq 30),我们可以考虑对于有限制的暴力,然后没限制的直接乘。
    具体方式为:
    (f_i)表示城市没有限制且学校没有限制有(i)人是蓝色的方案数,
    (g_i)表示学校没有限制有(i)人是鸭系的方案数,
    (h_{i,j})表示学校有限制有(i)人是蓝色,(j)人是鸭系的方案数。
    最后答案可以通过枚举(h)的两维状态然后再乘上(f,g)算出。
    这样是正确的是因为(f)算出了所有完全没限制的学校的颜色方案数,(g)算出了有城市限制或没限制的点的派系方案数,(h)算出了有限制的学校的方案数,同时决定了有城市限制的学校的派系方案数,这三者互不影响,却又能算出所有情况。
    至于(h)的转移,可以对于城市挨个转移,然后分别用两个数组表示强制颜色是不是蓝选出多少鸭系的方案数,dp 完再合并即可,具体实现详见代码。

    代码

    [SNOI2017]遗失的答案

    首先所有满足条件数必是(G)的约数,可以知道这些数的个数是不超过(800)的。
    然后对于所有(L,G)的限制就相当于对所有选出的数的质因子次数取(min/max)
    因为在可能所有数中质因子个数不超过(omega =8),那么我们可以将每一个数对应到一个长度为(2omega)二进制状态表示第i个数是否卡上界,以及是否卡下界。
    对这些状态统计高维前缀和,我们可以通过容斥得出总方案数。
    对于要求包含某个数的方案数,我们可以从高维前缀和中减去包含这个状态的方案,然后再通过容斥求出合法的方案数,用前面求出的总方案数减去这个方案数即为答案,具体怎么容斥参见代码。

    代码

    CF708E Student's Camp

    (h_{i,l,r})表示当前 dp 到第 i 行,当前行剩下([l,r])的概率。
    那么转移就是(h_{i,l,r}=P(l,r)sum_{[l,r]cap[l',r'] eq phi}f_{i-1,l',r'}),其中(P(l,r))为剩下([l,r])的概率,易得(P(l,r)=q_{l-1} imes q_{r-m}),其中(q_i={Kchoose i}p^i(1-p)^{K-i})

    (f_{i,r}=sum_{l=1}^rh_{i,l,r})
    (g_{i,l}=sum_{r=l}^Mh_{i,l,r})
    (F_{i,r}=sum_{R=1}^rf_{i,R})
    (G_{i,l}=sum_{L=l}^Mg_{i,L})

    那么有(h_{i,l,r}=P(l,r)(F_{i-1,M}-F_{i-1,l-1}-G_{i-1,r+1}))
    代入(f),有:

    [egin{aligned} f_{i,r}&=sum_{l=1}^r q_{l-1}q_{M-r}(F_{i-1,M}-F_{i-1,l-1}-G_{i-1,r+1})\ &=q_{M-r}ig(sum_{l=1}^rq_{l-1}(F_{i-1,M}-F_{i-1,l-1})-G_{i-1,r+1}sum_{l=1}^rq_{l-1}ig)\ g_{i,l}&=sum_{r=l}^M q_{l-1}q_{M-r}(F_{i-1,M}-F_{i-1,l-1}-G_{i-1,r+1})\ &=q_{l-1}ig(sum_{r=l}^Mq_{M-r}(F_{i-1,M}-G_{i-1,r+1})-F_{i-1,l-1}sum_{r=l}^Mq_{M-r}ig) end{aligned} ]

    最终答案就是(F_{N,M}),因为(F,G)对称,所以可以直接用(F)推出(G)

    代码

    [JOISC2020]建筑装饰 4

    (Nleq 2000)的 dp 比较显然,而这个 dp 的转移不好优化,考虑优化状态。
    (g_{i,0/1})表示到位置(i),选了(A/B)(A)最多有多少个,(f_{i,0/1})表示到位置(i),选了(A/B)(A)最少有多少个。
    那么如果存在(op,g_{N,op}leq Nleq f_{N,op})那么就证明有答案,随便输出下解即可。

    代码

    [LG4007]小 Y 和恐怖的奴隶主

    (f[i][a][b][c])表示在第(i)轮,除了 boss 有(a)(1)血的,(b)(2)血的,(c)(3)血的概率。

    转移随便分类讨论一波,矩阵快速幂优化下即可,注意矩阵要多开一维记期望,还要卡常。

    代码

    [CTS2019]氪金手游

    首先我们考虑一颗外向树的情况,令(x)的权值为(v)(x)子树和为(w'),全部的和为(w),那么可以等比数列求和算出(x)子树第一次选到(x)的概率为(frac{v}{w'}),发现和其他子树的概率无关,可以背包。

    再考虑一条链(1 ightarrow 2 ightarrow 3 ightarrow ... ightarrow ileftarrow i+1 ightarrow ... ightarrow n),这个的概率可以表示为(Pr(1 ightarrow i) imes Pr(i+1 ightarrow n)-Pr(1 ightarrow n)),就是这条边两边的概率相乘减去这条边为正的概率,我们可以把这个东西放到树上去,就可以容斥了。

    而树上 dp 时我们不用记录我们有多少条边是反着的,直接在这条边向上合并时乘上(-1)即可。

    代码

    [JLOI2016]成绩比较

    考虑容斥,设(f(i))表示至少(i)人被碾压的方案数,那么有

    [Ans=sum_{i=K}^N{i choose K}(-1)^if(i) ]

    那么有

    [egin{aligned} f(x)&={N-1choose x}prod_{i=1}^M{N-x-1choose R_i-1}sum_{k=1}^{U_i}k^{N-R_i}(U_i-k)^{R_i-1}\ end{aligned} ]

    (g(i)=sum_{k=1}^{U_i}k^{N-R_i}(U_i-k)^{R_i-1})
    稍微推下式子:

    [egin{aligned} g(i)&=sum_{k=1}^{U_i}k^{N-R_i}sum_{j=0}^{R_i-1}{R_i-1choose j}(-k)^jU_i^{R_i-1-j}\ &=sum_{j=0}^{R_i-1}(-1)^jU_i^{R_i-1-j}{ R_i-1choose j}sum_{k=1}^{U_i}k^{N-R_i+j} end{aligned} ]

    自然数幂和随便算算就完事了,总复杂度(O(n^3))

    代码

    CF1285F Classical?

    考虑确定(gcd),可以调和级数枚举然后把包含这个约数的所有数都从大到小抠出来,想办法对于(gcd=d)如何更新答案。

    假设我们现在维护了一个类似于单调栈的东西(s_{1,2...k}),那么当我们加进一个数(x)时我们如何更新呢?可以通过莫比乌斯反演算出这个栈中与(x)(gcd)(d)的数的个数(t),然后我们进行弹栈,因为保证了从小到大,所以我们如果(gcd=d)的话就直接更新答案然后弹栈并且(t)减一,否则一定满足当前栈顶的数后面更新的数比当前数(x)与前面的数都要小,也可以弹栈,这个过程持续到(t=0)

    代码

    CF1284E New Year and Castle Construction

    随便选(5)个点出来,

    (x_5)为这五个点所构成的凸包大小为(5)的方案数,
    (x_4)为这五个点所构成的凸包大小为(4)的方案数,
    (x_3)为这五个点所构成的凸包大小为(3)的方案数,
    那么答案就是(x_4+2x_3)

    而我们有随便选五个点的方案数是(x_3+x_4+x_5={nchoose 5}),乘上(5)再减去(5x_5+4x_4+3x_3)就是答案。
    考虑后面那一坨东西是什么,就是所有五个点凸包的边数之和。
    然后算这个的话就是算每一条边出现在凸包中的出现次数然后加起来,随便在一条直线的一半边选三个点都可以,极角排序后尺取就好了,但是这题卡时间只能用叉积排序。

    代码

    不等关系

    和 [CTS2019]氪金手游 一样的套路。
    假设我们只考虑<的限制,那么方案数就变为原序列被分成了若干个上升子串的方案数,令每段上升子串的长度为(len_i),那么方案数就是可重排列即(frac{n!}{prod len_i})

    而总方案数就是:只考虑<的方案数(-)>当成<的方案数,考虑容斥。
    (f_i)为长度为(i)的前缀除上(i!)的结果,(cnt_i)表示(s_1,s_2...s_i)>的数量,那么有

    [f_i=sum_{j=0}^{i-1}[s_j eq <](-1)^{cnt_{i-1}-cnt_j}f_j imesfrac 1{(i-j)!} ]

    分治NTT优化即可,复杂度(O(nlog^2 n))

    代码

    CF1349D Slime and Biscuits

    (E_x)为结束时饼干全在(x)手中的期望时间,那么答案为(sum_{i=1}^nE_i)
    (E'_x)为游戏结束当且仅当所有饼干均在(x)手中的期望时间。
    (P_x)为游戏结束时饼干全在(x)手中的概率,那么(sum_{i=1}^n P_i = 1)
    设常数(C)表示目前饼干全在(i)手中,全部转移到(j)手中的期望时间,那么对于所有(i,j),这个(C)都是相等的。
    那么我们有(E_x=E'_x-sum_{i eq x}(C imes P_i+E_i))
    移项得(sum_{i=1}^nE_i=E'_x-sum_{i eq x}C imes P_i)
    把所有项相加:(nsum_{i=1}^n E_i=nsum E'_i-C(n-1)sum P_i)
    由开始所述:(n imes ans=nsum E'_i-C(n-1))
    现在只要把所有饼干移到某个人手中的期望时间,令(f_i)表示目前一个人手中有(i)个饼干,把所有饼干移到他手上的期望时间,令(sum)为所有饼干数目之和,则有转移:
    ( egin{aligned} egin{cases} f_i=1+f_{i+1}frac 1{n-1}+f_ifrac{n-2}{n-1}&i=0\ f_i=1+f_{i+1}frac {sum-i}{sum} frac 1{n-1}+f_{i-1}frac i{sum}+f_i frac{sum-i}{sum} frac{n-2}{n-1}&iin (0,sum)\ f_i=0&i=sum end{cases} end{aligned} )
    用树上高斯消元的 trick ,设(f_i=Af_{i+1}+B_i),推下式子把(A_i,B_i)求出来即可,这里不再赘述。

    代码

    [CTS2019]随机立方体

    (f_i)表示至少有(i)个极大值的概率,(g_i)表示恰好有(i)个极大值的概率,令(lim=min(l,m,n))那么

    [f_i=sum_{j=i}^{lim} {lim choose j} g_i ]

    二项式反演得,

    [g_i=sum_{j=i}^{lim}(-1)^{j-i}{jchoose i} f_j ]

    所以我们现在要想办法求出(f_i)

    首先考虑我们按顺序加入(i)个极大值的坐标的方案数,就是(n^{underline i}m^{underline i}l^{underline i})
    然后你需要求出在已选出的坐标控制范围的并内,放入数字使得每个选定坐标均为自己控制范围内最大值的概率。
    因为我们之前是考虑过顺序的,所以我们可以改变一下放入的限制,即由自己控制范围内的最大值变为目前剩余空间中的最大值,那么每次放入后,只被当前最大值控制的区域就可以删掉了,就由(i)个点转化为了一个只有(i-1)个点的子问题了。
    (S_i=nml-(n-i)(m-i)(l-i)),那么

    [f_i=n^{underline i}m^{underline i}l^{underline i}prod_{j=1}^ifrac 1{S_j} ]

    后面那坨东西可以用求组合数的方法正推一遍,倒推一遍求出,就可以做到(O(n))了。

    代码

    [JOISC 2020 Day2] 遗迹

    考虑从后往前做这个问题,先将序列的最后一个数放进来,那么这个数在之后的操作中都不会改变了,然后我们加进第二个数,如果这个数和最后一个数冲突的话,这个数最终会减一,以此类推,每放进来一个数,如果它和之前的每个数的最终状态相冲突的话,它会一直减减直到它减到一个没出现的数字作为它的最终状态。
    那么我们有个状压dp,设(f_{i,s})表示做完(isim n),最终状态出现了数集(s)的方案数,那么每次放入一个数可以对于一段连续的(1)进行转移,如果放到了前缀(1)就意味着这个数没出现在最终状态。而两个相同的数我们不好确定它加入的顺序,所以我们认为相同高度的两个石柱本质不同,最后将答案除去(2^n)
    考虑优化这个状态,设(f_{i,j})表示做完(isim n),最终状态出现的数集(s)的前缀(1)的长度为(j)的方案数,这个地方的转移比较巧妙:
    假设目前我们放入一个数(x),如果(x>j+1),我们先不管它,直接继承前面的值。
    如果(x<j+1),那么它没出现在最终序列里,可以算出有多少个这样的数然后转移。
    最后是(x=j+1)的情况,我们考虑加入这个数后前缀(1)的长度增加了(k),转移的话就是选择前面被保留的数(共(cnt)个)中选择一些放在(x)后的(k-1)个数中,有({cnt-jchoose k-1})中选法,而这个数(x)的选择也有(k+1)种,有转移(f_{i,j+k}leftarrow f_{i,j} imes(k+1) imes {cnt-jchoose k-1} imes g_{k-1}),其中(g_{k-1})表示后面(k-1)个数放进来的方案数,最后的步骤就是怎么求它。

    (h_{i,j})表示(i)中高度,占了(j)个位置的方案数,枚举每次选(0,1,2)个这个数字(i),那么有
    (h_{i,j}=h_{i-1,j}+h_{i-1,j-1} imes2j+h_{i-1,j-2} imes j(j-1)),最后(g_k就等于h_{k,k}),这题就做完了,复杂度(O(n^3))

    代码

    [2018 集训队互测 Day 5]小 H 爱染色

    钦定(i)为编号最小的球,那么方案数为({n-ichoose m}^2-{n-i-1choose m}^2)
    (G(i)={ichoose m}^2)
    答案就是(sum_{i=0}^{n-m}F(i)(G(n-i)-G(n-i-1))),所以把(H(i)=sum_{i=0}^{n-m}F(i)G(n-i))求出来就好了。
    考虑到(F)是个(m)次多项式,(G)是一个(m^2)次多项式,(H)是把他们两个乘起来再加起来也就是(3m+1)次多项式,所以可以考虑把(H)的前(m)项求出来然后拉格朗日插值。
    注意到(F)只给了你前(m)项所以要用这题的方法插出前(3m+1)项的值,然后(mathcal {NTT})(F,G)卷起来就可以了,要注意优化常数,如果(G)某些前后缀是(0)那么(mathcal{NTT})就没必要开那么长。
    复杂度(O(mlog m))

    代码

    CF603E Pastoral Oddities

    发现当有方案当且仅当所有联通块大小均为偶数。
    证明的话就是一个联通块所有点的总度数为偶数,如果联通块大小为奇数的话奇数个点的度数和一定为奇数所以不满足。
    考虑用(lct)维护联通块,维护的东西是虚子树的(size)以及链上最大值以及最大值所在节点编号。
    如果合并之前未被合并的联通块直接用虚子树的(size)维护目前还有多少个大小为奇数的联通块,否则则找到最长的那条边看要不要删掉。最后在全局用一个数据结构维护所有的边,按边权从大到小排序后依次取出判断能不能断开即可,细节详见代码。

    代码

    [NOI2019]序列

    graph.png

    考虑如图所示的费用流连边,其中(C ightarrow D)的容量为(K-L)其余都为(1)(S ightarrow A_1,A_2...A_n)费用为(a_i)(B_1,B_2...B_n ightarrow T)费用为(b_i)其余都为(0)
    在这个图上限流为(K)跑一下结果显然是对的,考虑模拟这个费用流的过程以降低复杂度。

    如果(C ightarrow D)之间有边的话我们会先跑这条边,因为这样子可以任意选择(A,B)结果显然更优。
    考虑当(CD)满流时我们如何增广:
    ( ext{Case1: })选一条(B_i)已匹配的最大的(A_i),流(S ightarrow A_i ightarrow B_i ightarrow T),那么(D)出来会少一条流量,需要选一个最大的(B)出来保持流量平衡
    ( ext{Case2: })选一条(A_i)已匹配的最大的(B_i),流(S ightarrow A_i ightarrow B_i ightarrow T),那么(C)进来会少一条流量,需要选一个最大的(A)出来保持流量平衡
    ( ext{Case3: })选一条均未匹配过的和最大的(A_i+B_i),直接流过去即可

    注意到( ext{Case 1,2})可能会使(CD)流量减一,要优先流能使其减一的( ext{Case})
    综上所述,我们应用(5)个堆维护我们所需信息。

    代码

    CF671C Ultimate Weirdness of an Array

    注意到如果我们枚举(f)的取值的话是不好算的,所以我们可以算(fleq i)的方案数(h_i)然后用(h_i-h_{i-1})可以算出(f)(i)的方案数,答案即为(sum_{i=1}^{V} i imes (h_i-h_{i-1}))
    对于每个位置(i)我们记(nxt_i)表示以(i)为左端点(l),右端点(rgeq nxt_i)且保证(f(l,r)leq i)的最小的(r),那么(h_i=sum_{j=1}^n n-nxt_j+1),最开始(nxt_i=i)
    (d)从大往小扫,把所有是(d)的倍数的位置抠出来,记为(x_1,x_2...x_m),要保证(f(l,r)leq d-1)就只能保留一个至多位置,可以对于(iin[1,x_1])(nxt_i)(x_{m-1})(max)(iin(x_1,x_2])(x_m)(max)(iin(x_2,x_3]),对(n+1)(max),然后求整体的(sum nxt)即可,因为(nxt)单调所以不需要用吉利线段树,用 普通线段树区间赋值( ext{or};set) 维护即可。

    代码

    CF1270H Number of Components

    发现所有联通块都是一段区间,可以感性理解一下,这里不作证明。
    那么一段前缀和一段相邻的后缀如果被分为两个联通块,就意味着前缀(max)小于后缀(min)
    那么如果我们枚举(v),把(> v)的数设为(1)(leq v)的数设为(0),如果这个序列呈现出前缀全部是(1)后缀全部是(0),就意味着联通块数目需要加上(1)
    考虑维护对于每个(v)(0/1)交界的个数,那么需要将位于原序列位置(a_0)的数设为(infty)(a_{n+1})的数设为(0),否则会出现前后缀(01)反过来的情况。
    对于相邻的数(a_i,a_{i+1}),会对([min(a_i,a_{i+1}),max(a_i,a_{i+1})))加上(1)的贡献,用线段树维护值(v)(01)交界数最小值以及最小值出现次数即可,注意必须要保证线段树中的(v)必须在原序列中出现过才能够计算贡献。

    代码

    [JOISC 2020 Day1]扫除

    离线。
    把每个扫除的操作当做一个点,那么每次所要查询的就是从一个点插入到这次查询经过这段区间后这个点会变为多少,我们考虑将这段区间打在线段树上,那么这段操作就对应了(log)段小区间。
    如果只有向一边的操作,那么操作间是互不影响的。
    而对于一个向右的长度为(h)扫除以及在它之前的向上的长度为(x)的扫除,这个(h)实际上只会影响到高度(leq h)且横坐标在([x+1,n-h])的点,可以先预处理出这样的操作,然后对于每个区间中的查询,对(h)排序单调指针扫然后横坐标用线段树维护。
    细节建议参照代码,复杂度两只(log)

    代码

    [JOISC 2020 Day2]有趣的 Joitter 交友

    考虑将互相关注的点缩成一个联通块,然后维护这个联通块的(size)和单向边进来的点,和这个联通块出去的点,那么算答案是容易的。
    合并时也不是很难做,但是如果发生连锁反应要递归处理。

    代码

    [JOISC 2020 Day3]星座 3

    (mathsf{color{black}{M}color{red}{\_sea}})题解讲得很清楚,就直接蒯他的了/kel:

    考虑一个贪心。从下往上扫,同时每个位置维护一下它下方保留了的不能与它共存的权值和,对于每颗星星留一个权值大的即可。

    考虑每颗星星上方不能与它共存的星星的范围,显然是往左、往右走到第一栋比它高的大楼的范围。

    用并查集维护每个点往左、往右最远能走到哪里,然后区间加一下就好了。

    代码

    [JOISC 2020 Day4]首都城市

    考虑把每个颜色的点抠出来向他们路径上的其他的颜色连边,边(x ightarrow y)表示如果(x)要作为首都则必须合并(y),连完边后缩点求出的最小的无出度的点的(size)即为答案。
    这样子连边是(n^2)的,用(mathcal{ST})表优化下就是一个(log)的了。

    代码

    [JOISC 2020 Day4]治疗计划

    考虑两个区间([L_i,R_i],[L_j,R_j](R_igeq L_j))是否能拼成一个区间的情况就是(R_i-L_j+1geq |T_i-T_j|)

    所有区间按(T)排序,把绝对值拆开后用线段树维护绝对值为正负的两种情况,考虑以与(1)相连的区间跑最短路直到与(n)相连,(dijkstra)时用线段树找点,因为第一次更新时坑定最优所以当找到这个点时直接把这个点从线段树上删掉就保证了复杂度。

    代码

    [十二省联考 2019]字符串问题

    考虑怎么判断是不是无穷,可以先从(A)按照支配顺序向(B)连边,然后(B)如果是(A)中某个串的子串就连回(A),看下有没有环就行了。发现如果是求最长的话就是在这个无环的 DAG 上拓扑排序。

    考虑后缀排序后优化连边,直接用普通的线段树优化连边会有问题,因为(B)连向(A)的边可能有(|B_j|geq |A_i|),但是这样还是有(80) pts。发现可以按照(A,B)的长度从大到小排序依次加入时用主席树优化连边就可以解决这个问题了。

    代码

    CF1063F String Journey

    (k)个串的长度最优时一定是(k,k-1...2,1),否则一定可以构造成这种情况。
    (f_i)表示(isim n)(i)结尾的最优答案,那么有(f_{i}-1leq f_{i+1}Leftrightarrow f_ileq f_{i+1}+1),所以从(i+1 ightarrow i)时,我们可以枚举(f_i)的取值,这样子均摊下来复杂度是正确的。
    现在的问题就是判断(f_i)取某个值(k)是否可行,如果(f_i=k)可行的话,则必须要满足条件:
    存在(jgeq i+k)(f_jgeq k-1),且(lcp(suf_i,suf_j)geq k-1; ext{or};lcp(suf_{i+1},suf_j)),因为无论怎样(i+k)都是单调不升的,所以可以拿单调指针扫,而(lcp)后缀排序之后对应一段区间,拿个线段树维护(f)的区间即可。

    代码

    [集训队作业2018]喂鸽子

    在这里

    [SNOI2019]通信

    大概像这样建个边:

    graph (2).png

    然后跑费用流(容量和流量未标出)
    考虑如何优化连边:
    考虑CDQ分治,然后考虑右边区间向左边区间连边,将左边的点按照权值大小排好序之后前缀优化连边就好了。

    [JSOI2019]绝地反击

    以前写过一遍了
    这里提供两种方法:

    方法一:
    是我自己想到的,对于圆上点的排列,假设所有点均由正方向旋转度数(alpha),那么最优解函数(f(alpha))一定为一个单峰函数。
    三分(alpha)后再二分时间(t),将到达时间(leq mid)的我方舰队向敌方母舰连边,跑二分图匹配看是否有完美匹配即可。
    (别人写的能过,我不知道为什么自己写的过不了/kk
    代码

    方法二:
    二分时间(t),那么对于每艘战舰在圆弧上的交点至多只有(2)个,而对于旋转,可以发现本质不同的转法一定是有一个点转到了这(2n)个交点中的其中一个。
    我们将这(2n)个转角极角排序(这(2n)个点可用余弦定理算出),则每次改变转角最多加入或删除一组匹配,手动模拟退流即可,复杂度(O(二分图匹配 imeslog n))
    代码

    [GXOI/GZOI2019]旅行者

    正反图各跑一遍最短路,然后枚举边计算答案即可。
    代码

    [NOI2016]网格

    显然答案最大为(2),那么对于这个图我们只有(-1,0,1,2)四种情况,考虑依次判断。
    对于(-1),如果只有一个空位或者有两个空位且空位相邻则满足。
    对于(0),可以选取每个已有的点周围的八连通的空位然后判断这些空位组成的四连通块是否连通即可,这里我选取的是周围的(24)个点原因下面讲。
    对于(1),就是判断我们所选取出的点中是否存在割点,但是如果我们仅仅选出八连通块,会出现这样的情况导致误判:

    .00
    .X1
    .00
    

    所以我们选出(5 imes 5)的范围,而且只有割点是周围的八个点才行,要不然会出现这样子的情况:

    100..
    000..
    00X00
    ..000
    ..001
    

    注意特判(n=1 ext{or} m=1)
    不满足以上情况就是(-2)
    代码细节有点多。
    代码

    [SDOI2017]树点涂色

    在这里

    [清华集训2014]主旋律

    这种题目考虑 容斥+状压。
    (f_i)表示点集(i)所构成强连通子图的方案数,那么可以拿(2^{|E_i|})减去不满足要求的,其中(E_i)表示(i)中的边数。
    考虑枚举入度为(0)的强连通子图的联通块,但是无法保证所枚举的联通块一定构成一个强连通分量,所以考虑进一步容斥。
    发现容斥系数只与所枚举的的联通块所构成的强连通分量的个数有关,因此可以设(g_i)表示点集(i),构成奇数个强连通分量的方案数(-)构成偶数个强连通分量的方案数。
    应为每次新加进一个强连通分量容斥系数就会乘上(-1),那么有转移(g_i=-sum_{jsubset i}f_jcdot g_{i-j}),注意(g)不包含只含有一个强连通分量的情况,以便下面的转移。
    再枚举某一个入度为(0)的联通块,那么又可以得到(f_i=2^{|E_i|}-sum_{jsubset i}g_jcdot 2^{|E_i|-|E_j|})
    最后再将(f_i)算入(g_i)即可,答案就是(f_{全集})
    代码

    [NOI2018]归程

    跑一遍(1)到每个点的最短路,然后按照海拔从小到大建克鲁斯卡尔重构树,倍增找到在克鲁斯卡尔重构树上一个点可以跳到的最浅的点然后查下子树最短路的最小值即可。
    代码

    [NOI2017]游戏

    没有x我们可以直接( ext{2-sat})输出方案,有x时只需枚举第(i)种赛道不适合A车或B车,因为这样子就可以包含不适合C车的情况了,然后直接(2^d)爆搜一下就好了,具体的连边方式什么的直接看代码就好了。
    代码

    [雅礼集训2018]仙人掌

    先考虑一棵树的情况,令(f_{x,0/1})表示考虑(x)与其子节点的边,(x)的出边是(1)否(0)已满的方案数。
    那么转移是个背包,其中对于(vin son_x)如果增加(x)的一条出边的方案数是(f_{v,0}+f_{v,1})否则是(f_{v,0}),那么这个背包用(mathcal {NTT})转移一下就可以做到(O(nlog ^2 n))

    仙人掌的情况我们考虑圆方树上的 dp,令(f_{x,0/1/2}),具体表示的是什么我们分情况讨论。

    如果(x)是一个圆点,且它的父亲也是圆点,(f_{x,0/1})表示它的父亲节点与它之间的边是否是指向(x)的方案数。
    如果(x)是一个方点,且它的父亲是圆点,(f_{x,0/1/2})表示这个环的最上面的两条边(就是与(fa)直接相连的那两条),不指向fa的方案数。
    如果(x)是一个圆点,且它的父亲是方点,(f_{x,0/1/2})表示的就是它所在的环上与它直接相邻的两条边,指向它自己的边的方案数。

    转移的话如果(x)是圆点,可以和树一样NTT,如果是方点的话,枚举环中一条边的方向即可转移,具体细节详见代码。
    代码

    [雅礼集训2018]Magic

    考虑容斥,设(f_i)表示恰好(i)的答案,(g_i)表示至少包含(i)个魔术对的序列数。则:

    [f_i=sum_{j=i}^n {jchoose i}g_jLeftrightarrow g_i=sum_{j=i}^n (-1)^{j-i}{jchoose i}f_j ]

    我们考虑将所有球分配一个标号,那么最后的答案要乘上一个(prod frac 1{a_i})
    (R_{i,j})表示前(i)种球至少有(j)对的方案数,(h_{i,j})表示第(i)种球至少有(j)对的方案数。
    那么最后(R)就是所有(h)的卷积,考虑怎么求(h)
    (h_{i,j})就是从(a_i)个求中选出(a_i-j)个,然后剩下(j)个球依次接到之前放的某个球的前面,方案数为

    [h_{i, j} = {a_ichoose j} frac{(a_i - 1)!}{(a_i - j - 1)!} ]

    可以分治NTT将所有(h)卷起来得到(R)
    注意一下最后的(R_{m,i})并不是(g_i),因为根据我们刚才的转移,剩下的(n-i)个球可以随便选出,所以还要乘上((n-i)!)
    代码

    [集训队互测2019]国际象棋

    最裸的高斯消元是(O(n^3m^3))的。
    考虑将第一、二行和第一列的所有元素拉出来做主元,那么我们从上至下、从左至右处理每一个点时,它的期望只有(4)号位移的点你是没有表示出来的,所以我们可以通过这个点的期望减去其他(7)个点算出,最后棋盘外对应有几个(0)的地方拉出来消元就可以把这些主元全解出来了。
    代码

  • 相关阅读:
    .net中AjaxPro的简单使用
    关于 Activity 中 startActivityForResult 和 onActivityResult
    产品经理值得读的12本书
    存储过程中使用事务与try catch
    扁平化数据解构转化成树形结构
    JavaScript编程精解
    泛型
    OATS:Orthogonal Array Testing Strategy
    等价类划分
    C# homework
  • 原文地址:https://www.cnblogs.com/heyujun/p/12769930.html
Copyright © 2020-2023  润新知