2018年各大赛事题解

大多数题解都是口胡,不保证正确性,有错请指出,谢谢。

CQOI2018

除了 “交错序列” 和 “九连环” 两道数学题以外,全是板子题,遭不住了。

  • 破解D-H协议

    BSGS 板子题,时间复杂度 O(nPlogP)O(nP)

  • 社交网络

    矩阵树定理板子题,时间复杂度 O(n3)

  • 交错序列

    由于 x+y=n ,所以权值转化为 (ny)ayb=i=0a(1)ai(ai)niya+bi 。只需要求出一个序列的权值是 yi,i[a,a+b] 的所有答案即可。

    经典的 dp :fi,j,0/1 表示长度为 i 的序列,权值是 yj ,最后一个数是 0/1 的权值和。转移:

    fi,j,0=fi1,j,0+fi1,j,1

    fi,j,1=k=0j(jk)fi1,k,0

    矩阵加速转移即可,时间复杂度 O((a+b)3logn)

    也可以直接枚举 1 的个数,写出最后的答案:

    i=0n+12(ni+1i)(ni)aib

    线性筛 ia,ib 即可,时间复杂度 O(n(1+loga+logblogn))

    洛谷题解区有人用 EI 科技做到了时间复杂度 O(logn+(a+b)log(a+b)) ,但我不会。

  • 解锁屏幕

    显然的状压 dp :fi,j 表示经过的点集为 i ,最后一个点是 j 的方案数。转移时枚举下一个点 k,预处理连 (j,k) 需要事先经过哪些点即可。时间复杂度 O(2nn2) ,除去无用状态后可以通过。

  • 九连环

    通过观察样例,可以盯出来答案是 2n+13 ,快速幂加 FFT 实现高精度计算即可,时间复杂度 O(mnlogn)

    详细的推导过程:两个规则都是允许随意上下,那么上 n 连环和下 n 连环互为逆过程,所以上 n 连环和下 n 连环的步数是一样的。观察四连环的拆卸过程,发现整个过程可以归纳为:

    111...1111000...0001000..000111...111000...00

    也就是拆 n 连环,先拆 n2 连环,再操作一步,再上 n2 连环,再拆 n1 连环。设 fn 为拆 n 连环所需的步数,那么:

    fn=fn1+2fn2+1

    f0=0,f1=1

    稍微变形一下便容易得到:

    fn+fn1+1=2(fn1+fn2+1)

    fn+fn1=2n1

    fn=fn2+2n1

    分奇偶讨论一下即可得到结论。

  • 异或序列

    区间异或和转化成前缀异或和后就可以直接莫队了,时间复杂度 O(nm)

AHOI2018初中组

虽然是初中组,但整体思维难度比 CQOI 高。“根式化简” 需要发现 x14 的特殊性质;“分组” 的原题是简单的贪心,加强版是经典套路;“球球的排列” 通过经典的转化后变成经典的计数问题。

  • 报名签到

    同题目名字,签到题,答案为 i=1n1max(ai,ai+1)

  • 根式化简

    显然 ax13 ,但直接 O(n(x13logx+logx)) 并不能过。再进一步观察,可以发现把所有小于等于 x14 的质数都尝试了之后,剩下的未被分解的 x 要么是一个完全立方数(并且形如 p3p 是质数),要么不含立方因子。所以提前处理一个所有小于等于 x13 的质数的立方表,在上面二分查找一下即可,时间复杂度 O(n(x14logx+logx))

  • 分组

    排序后贪心地模拟分组情况就好了,优先把当前的人加到之前人数最小的组里,剩余的没分配到的组就抛弃掉,开个队列维护就好。时间复杂度 O(nlogn)

    值得注意的是(其实就是我读错题了),此题不需要满足分组在原序列连续。如果加了这个限制的话,可以二分答案后,用单调栈加线段树维护 dp 可以转移的所有位置来快速计算 dp 值,时间复杂度 O(nlog2n) ,因为跟此题关系不大,不详细叙述。

  • 球球的排列

    经典题。把所有数的平方因子去掉之后,问题转化成每个元素有一个颜色,问有多少排列满足相邻元素颜色不同。O(n3) 做法大概就是按颜色依次插入球,然后 dp:fi,j,k 表示当前插入第 i 个球,之前的颜色里,相邻的有 j 对,当前的颜色里,相邻的有 k 对的方案数。转移时分类讨论即可。

    然而这个问题有熟知的 O(nlog2n) 做法。以下认为颜色相同的球没有区别,最后方案数乘一些阶乘就好了。设恰好有 i 对相邻球颜色相同的排列个数为 Gi ,至少有 i 对相邻球颜色相同的排列个数为 Fi ,由二项式反演:

    Fi=j=in1(n1j)Gj

    Gi=j=in1(1)ji(n1j)Fj

    下面求 F 。记 m 为颜色种类数, ci 的为第 i 种球的个数,设 hi,j 为把第 i 种球分成若干个连续段,其中相邻的球有 j 组的方案数,H^i(z)hi 的 EGF。那么容易得到 H^i(z)=j=0ci1(ci1j)zjj! 。所以 Fi=n![zi]i=1mHi(z),分治 NTT 计算即可。

HNOI/AHOI2018

这套题有很多不错的结论和经典的套路。“寻宝游戏” 在一个熟知的结论下进行构造转化,得到一个更强大的结论;“转盘” 很容易看出来需要用线段树维护单调栈,难点在于如何转化与推式子;“毒瘤” 只要抓住了非树边不多的经典性质,便可拿到较高分数,对于建出虚树后虚树上的边上的方案可以预处理可以作为一个套路;“游戏” 的结论并不难证明,但很优雅;“排列” 通过题意转化可以得到一个经典的贪心问题;“道路” 便显得是一道很水的树形 dp 。

  • 寻宝游戏

    一个经典的结论是某一位最后是 0/1 取决于最后一次 "&0"/"|1",其他操作对值不产生任何影响。有这个结论之后,还可以构造出一个更强的结论:对某一位 i 来说,记它的所有操作于它的值拼接而成的二进制表示为 bi,低位为先操作的值,高位为后操作的值。令 & 为 1,| 为 0 ,那么每种插入的运算符方案也对应一个二进制数 x ,高低位同上。那么,第 i 位最后的结果是 1 当且仅当 x<bi 。这个结论是不难证明的。所以可以先对于每一位的二进制数基数排序一下,查询的时候找到所有 1 的位的最小的数 x 和所有 0 的位的最大的数 y ,答案即为 max(xy,0)

  • 转盘

    可以发现一开始就在起点等和在每个点分别等的效果是一样的,所以算出最早的出发时间,再加上走一圈的时间 n1 即是答案。以下破环成链。枚举起点 i ,那么出发时间需满足对于 j[i,i+n1],t+jiTj ,所以

    t=maxj=ii+n1(Tjj+i)

    于是答案为:

    n1+mini=1n(i+maxj=ij=i+n1(Tjj))

    由于 Tjj>Tj+n(j+n) 所以可以把区间 max 换成后缀 max

    n1+mini=1n(i+maxj=ij=2n(Tjj))

    用一个单调栈维护 Tjj 的后缀最大值,若单调栈上第 k 个元素在原序列的位置是 pk ,那么其对答案的贡献就是 n+pk+Tpk+1pk+1 (强制令 T0=)。线段树维护单调栈就好了,时间复杂度 O(nlogn+qlog2n)

  • 毒瘤

    经典套路,非树边很少,直接枚举每条边的某个点选还是不选,然后跑树形 dp ,时间复杂度 O(2mnn) 。考虑优化:称非树边两端的点为关键点,那么其实我们只用关心关键点形成的虚树上每个点是否被选,而只要虚树上的点的选法确定了,非虚树上点的方案是可以预处理出来再乘法原理的。所以时间复杂度降到了 O(n+(mn)22(mn))

  • 游戏

    首先把没有锁的门连接的房间缩点,设房间 i 能走到的区间为 [li,ri]。然后需要发现一个结论:如果 x 能走到 y ,那么 lxlyryrx 。证明比较简单,不妨设 y<x ,那么说明 (y,y+1) 的钥匙的位置 >y ,所以 ry=y ,那当 x 走到 y 的时候,能拿到的钥匙一定都包含了 y 能拿到的钥匙,能走到的房间一定也都包含 y 能走到的房间。

    于是,记忆化搜索并暴力扩展每个房间的 [l,r] 即可,时间复杂度 O(n+p)

  • 排列

    首先需要把问题转化,把限制的形式变形一下:

    pk=apjk<j

    k=ajpk1<pj1

    aj0paj1<pj1

    把权值也变成 p1 的形式:

    i=1npi1wi

    所以问题转化成了有一张图,如果 ai0 那么 aii 连一条有向边,给每个点赋一个互不相同的点权 pi1[1,n] ,要求如果存在边 (u,v) 那么 pu1<pv1 ,问权值最大是多少。

    容易发现,如果这张图构成了环就无解,否则就一定是一棵外向森林,为了方便,加一个 0 号点连所有根,这样就是一棵外向树。

    考虑贪心:逐步确定每个点的前驱(权值比它刚好小 1 的点)。对于权值最小的点,它的前驱一定是它的父亲,但如果父亲已经作为某个点的前驱了的话,那么它只能接在它父亲这条序列的后面。这个过程相当于是把两条序列拼起来。假设序列 u 的长度是 su ,权值和是 wu ,那么序列 v 接到 u 后面产生的贡献就是 suwv
    所以当 suwv>svwu 的时候,vu 后面是更优的,就优先接 u 。上式等价于 wvsv>wusu,于是以 wusu 为关键字,把所有序列扔到堆里,每次取出 wusu 最小的序列,和父亲的序列合并即可。时间复杂度 O(nlogn)

  • 道路

    简单树形 dp :fu,i,j 表示根到 u 的路径上有 i 条没翻修的公路和 j 条没翻修的铁路,u 子树内的乡村的最小不便利值。如果是叶子结点,可以直接根据公式算出来;如果是非叶子结点,讨论是翻修哪条边,取 min 即可。时间复杂度 O(nd2),d=40

TJOI2018

整体来说比较水,“数学计算” 比较基础,只要见过这种思想就会;“智力竞赛”“游园会”“异或”“教科书般的亵渎” 都很板,也都很典;“碱基序列” 的最大难度是读题,学好高中生物有助于理解题意。

  • 数学计算

    由于 x 在模 M 意义下可能没有逆元,所以直接做不太行。一个想法是仿照线段树分治,对每一个乘法操作找到影响区间,在时间线段树上区间乘一下就好了,时间复杂度 O(QlogQ)

  • 智力竞赛

    二分答案后就变成了可重点的最小点覆盖,提前传递闭包后跑二分图匹配即可。也可以二分答案后跑有源汇上下界可行流,建模方式比较简单。

  • 游园会

    dp 套 dp 板子题。内层 dp 是求 lcs 的 dp :fi 表示奖章串的前 i 个字符与当前串的 lcs。把所有的状态和转移预处理出来即可。注意到 fifi1+1 ,所以只有 2K 种状态。外层再额外记录一下当前串的末尾长什么样子来判掉 "NOI" 的情况即可,时间复杂度 O(2KN)

  • 碱基序列

    直接 dp + KMP/hash 判断即可。

  • 异或

    dfs 序上和根到每个点上分别建一棵可持久化 Trie 即可,时间复杂度 O((n+q)logv)

  • 教科书般的亵渎

    稍微分析一下便知道是要求一些自然数幂和,常用的方法是递推或者拉格朗日插值。题解区提到了 伯努利数 ,也是一种优秀的解法。

JXOI2018

“排序问题” 和 “游戏” 是很基础的组合问题,“守卫” 是一道有很强误导性,但方向对了就很简单的区间 dp 。

  • 排序问题

    设每个数的出现次数是 ci ,那么显然答案是 (n+m)(n+m)!ici! 。统计一下 [l,r] 内的数出现次数为 i 的数有多少个,贪心地补就好了。时间复杂度 O(m+n)

  • 游戏

    容易发现当且仅当一个固定的集合 S 里的办公室被检查后,所有人都被检查了。S 可以埃筛求出,也可以线筛预处理每个数的最小质因子然后判断是否在 S 里。现在问题转化成了所有长度为 n 的排列中,S 中的数最晚出现位置的和。

    方法一:枚举最晚出现位置,然后组合数算:

    |S|!(n|S|)!i=|S|ni(i1|S|1)

    方法二:经典结论是最晚位置的期望是 (n+1)|S||S|+1 ,所以答案就是:

    |S||S|+1(n+1)!

    通过化简上面的式子也可以得到。时间复杂度 O(nloglogn)O(n)

  • 守卫

    注意到只能往左看,所以对于区间 [l,r] 来说,r 必须安排守卫。那么考虑 dp :fl,r 为区间 [l,r] 的答案。假设 r 能看到的所有位置是 p1,p2,,pk(单调递增),那么就把 [l,r] 分成了若干段,每一段是一个子问题。要注意的是,右端点既可以是 pi 也可以是 pi1 。外层顺序枚举 r ,内层倒序枚举 l ,维护 p 即可做到时间复杂度 O(n2)

FJOI2018

“领导集团问题” 算是一个经典的树上 LIS 问题,可以仿用序列上 LIS 中的其中一种方法计算;“所罗门王的宝藏” 是一道很套路的题;“邮递员问题” 是一道恶心的分类讨论 dp 题,确定路线的整体的走向以及大量的细节是难点。

  • 领导集团问题

    树上 LIS 。

    方法一:在每个节点 u 上维护一个 multiset (权值从大到小)fu,其中的第 i 个元素 fu,i 表示 u 子树内,所有大小为 i 的方案中,最小权值最大的权值是多少,答案即是 |f1| 。当要计算 fu 时,先遍历其所有儿子 vfv 直接互不影响,所以可以先令 fu=vson(u)fv ,这里的 是集合并,可以重复元素。再考虑 u 的贡献。对于所有 fu,iwu 的方案,u 对其不产生影响。如果存在一个 fu,i<wu ,找到最大的一个,令 fu,iwu ,因为此时把 wu 放到大小为 i 的方案里最小权值更大;否则直接把 wu 加到 fu 里即可。集合合并时采用启发式合并可以做到时间复杂度 O(nlog2n) ,用线段树合并实现即可做到 O(nlogn)

    方法二:线段树合并维护 dp :fu,i 表示 u 子树内最小值是 i 的最大方案。合并两个子树时:

    fu,imax{fu,i+maxjifv,j,fv,i+maxjifu,j}

    插入点 u 时:

    fu,wu1+vson(u)maxiwufv,i

    第一个操作在线段树合并时维护两个后缀最大值即可;第二个操作先后缀查询再单点修改即可。时间复杂度 O(nlogn)

  • 所罗门王的宝藏

    设第 i 行操作出来的数为 xi ,第 j 列操作出来的数为 yj ,那么 (i,j)c 等价于 xi+yj=c 。建图,每一行每一列是一个图,一个限制是一条边,显然这张图是二分图,每个连通块合法就合法。考虑一个连通块:随便弄一棵生成树,令根的值为 z ,那么所有点的权值都可以表示成 kz+b,k{1,1} 的形式。对于每条非树边判断其是否合法即可,容易证明判断时 z 的系数恒为 0 。时间复杂度 O(T(n+m+k))

  • 邮递员问题

    此题的难点在于如何归纳一条可能的答案路径的走向。为了方便,令两条平行线分别为第 0 行和第 1 行,起点在第 0 行。先考虑一种最简单的情况:起点在最左边,终点在最右边,那么比较显然的是任意时刻经过的点是第 0 行的一段前缀和第 1 行的一段前缀,dp 即可。一般情况下,从起点出发会先走到最左边或者最右边,然后用上面的方法走,最后走到终点。下面考虑起点如何走到最左边(终点以及最右边同理):

    以上两种情况是必要的,其中 s 是起点,i 是枚举的中转点。为了方便,dp 中涉及路径的起点统一都在第 1 行第 1 个点。上述两种情况其实表明的是一开始先走一段第 0 行的点,但又没有可能也先走一段第 1 行的点呢?也就是下图的情况:

    答案是否定的,对于第一种情况可以沿绿色箭头的方向走,这样就变成了上面 s=i 的情况;对于第二种情况,走绿色的路径显然比原来的路径优。

    总结一下,先从起点和终点出发通过最上面两种方式走到最左边/最右边,然后按照一开始说的任意时刻经过的点第 0 行的一段前缀和第 1 行的一段前缀。dp 分类讨论即可,时间复杂度 O(nm)

HAOI2018

“染色” 是一道典型的二项式反演数数题,式子不难推。“苹果树” 是一道套路题,后半部分的数数方法可以通过观察样例或者找规律,归纳出来。“字串覆盖” 是一道毒瘤字符串 “根号” 分治题,方法比较套路,可以练习板子。“反色游戏” 也是一道套路题,但是后半部分的广义圆方树需要捋清楚。“奇怪的背包” 需要先发现一个结论,把问题的值域缩小,就可以背包了。可以发现此题的背包做的实际就是 gcd 卷积,所以可以进一步优化,利用 Pollard-Rho 可以让此题的 P 开到 1018

  • 染色

    恰好不好做,二项式反演转化成至少。设 Fi 为至少有 i 种颜色出现了 S 次的方案数,Gi 为恰好有 i 种颜色出现了 S 次的方案数。根据二项式反演:

    Fi=j=iM(ji)Gj

    Gi=j=iM(1)ji(ji)Fj

    稍微变形一下:

    Gi=1i!j=iM(1)ji(ji)!j!Fj

    是差卷积的形式,只需要算出 F 即可 O(MlogM) 算出 G

    F 的计算是比较基础的组合问题,大致是先选哪些颜色被钦定,再安排位置,最后没被限制的位置涂没被钦定的颜色。

    Fi=(Mi)N!(S!)i(NiS)!(Mi)NiS

    时间复杂度 O(N+MlogN+MlogM)

  • 苹果树

    观察样例可以发现,第 i 个点有 i 个位置可以放,所以总共有 n! 种二叉树。树上两两节点距离之和的经典处理方法是枚举边,假设深度较浅的点是 i ,其子树大小为 j ,那么经过这条边的点对数为 j(nj) 。考虑有多少棵树满足此要求:首先前 i 个点随便放,共有 i! 种放法;然后 i 子树内有 j 个点,所以要从 i+1n 中选 j1 个点出来随便放,也就是 (nij1)j!;最后剩余了 nji+1 个点,它们不能放在 i 子树里,类似之前的分析,第一个点有 i1 种放法,第二个点有 i 种放法...所以方案数是 (nj1)!(i2)! 。整个式子写出来并去掉分母便得到:

    i=2nj=1ni+1j(nj)i!(nij1)j!(nj1)!(i2)!

    i=2nj=1ni+1j(nj)(nij1)(nj1)!i(i1)

    时间复杂度 O(n2)

  • 字串覆盖

    看数据范围便知道至少要设计两种算法,但显然的一点是从左到右贪心是必须的。

    • rl2000 ,合法的串很少,只要能快速找到下一个合法的串即可。于是对 A,B 建广义 SAM(也可以只对 A 建 SAM,这里为了后文方便),可持久化线段树合并预处理 endpos 集合。一次询问倍增定位到 B[l:r] 所在的节点,在其线段树上二分即可,时间复杂度 O(qnrllogn)

    • rl50 ,长度很小,考虑对每个长度 len 的询问分别处理。要快速找到区间内所有不交的子串的信息和,很套路地,考虑倍增。fi,j/gi,j 表示在 A 上从 i 出发,找 2j 个互不相交的与 A[i:i+len1] 相同的子串的最后位置/信息和。fi,0gi,0 可以类似第一种方法在线段树上二分算。查询的时候要先找到 B[l:r]A 中第一次出现的位置,同样线段树上二分即可。为了节约空间,需要每种长度分别做,这样只用开一个倍增数组。时间复杂度 O((n+q)(rl)logn)

    • 50<rl<2000,询问个数不多,且均匀随机,所以直接用方法一跑即可。

  • 反色游戏

    先考虑不带删除的情况,这是一个经典问题。连通块之间互不影响,考虑其中一个连通块。反转操作不影响整体奇偶性,所以一个连通块的黑色点数为奇数则无解。否则随便取出其一棵生成树,从叶子开始贪心地满足当前节点的要求,最后只有根无法做出选择,但因为保证了黑色点数是偶数,所以最后根一定也是黑色。于是对于任意一种局面,都可以通过调整树边来使局面合法,于是答案就是 2mn+c ,其中 c 是连通块数,nm+c 其实就是非树边的数量。

    如果删点,那么有可能改变图的连通性,所以先求出点双,建出广义圆方树。一个点 u 在广义圆方树上的度数即是它连接的连通块数 t,所以删去它后,cc+t1 。再在广义圆方树上 dfs 求出子树内的黑色点个数奇偶性,显然必须满足 u 的所有子树以及上方的父亲子树的黑色点个数都为偶数,在判断一下 u 的颜色与全局的要求即可。时间复杂度 O(n+m)

  • 奇怪的背包

    根据裴蜀定理,单独用物品 Vi 能表示出来的重量是 gcd(Vi,P) 的整数倍,所以可以把 Vi 替换成 gcd(Vi,P) 。同理询问的 wi 能被表示出来当且仅当 gcd(wi,P) 能被表示出来,所以 wi 也替换成 gcd(wi,P) 。这样,涉及到的数的个数就只有 σ(P)1344 个。然后直接对每个物品做背包就可以通过,再处理一下询问 x 会涉及到的约数 y(y|x) ,把 y 的贡献加到 x 上即可,转移时有一个 gcd 的复杂度,总复杂度是 O(P+σ(P)2logP+nlogP+qlogP) 。考虑到这个背包相当于是对所有物品做了一遍 gcd 卷积,所以预处理一下 P 的质因子,然后对每个物品先做一遍 Dirichlet 后缀和,对应相乘后再差分回去,就得到了原来的背包的结果。之后处理 x 所有约数的答案也就是再做一遍 Dirichlet 前缀和。时间复杂度 O(P+min(n,σ(P))σ(P)ω(P)+nlogP+qlogP)

    其实还可以对 gcd 卷积的过程再优化。一个物品 vi=gcd(Vi,P) 有选和不选两种选择,不选的话相当于是选另一个体积为 P 的物品,所以在 P 处有贡献,Dirichlet 后缀和后所有位置都有 1 的贡献;选的话就在 vi 处有贡献,所有 x|vi 的位置的贡献会变成 2 。发现只有这一类贡献是有用的,即所有 x|vi 的位置会乘 2 。那么一开始直接把所有 vi 扔到一个数组里跑 Dirichlet 后缀和,算出每个位置的值是 c ,那么该位置实际的值就是 2c ,后面的都是一样的了。时间复杂度 O(P+σ(P)ω(P)+nlogP+qlogP)

ZJOI2018

暂时会两个题,其他题先咕了。

  • 线图

    先考虑 k 比较小的情况:

    L(G):m

    L2(G):i=1n(degi2)

    L3(G):(u,v)(degu+degv22)

    L4(G):(u,v),(u,w)(2degu+degv+degw62)

    前三个比较简单。L4(G)=L3(L(G)) ,而 L(G) 中的每一个点对应 G 中的一条边,所以把 L3(G) 式子中枚举的所有点换成边即可。至于计算,L4(G) 可以通过预处理 fu=(u,v)(degu+degv),gu=(u,v)(degu+degv)2 ,然后把式子拆开计算。时间复杂度均为 O(n) 。为了拿分的话,可以暴力把 L(G) 建出来,然后跑 L4(G) ,得到 50 分。

    k 比较大的时候,再化式子已经不太可能,考虑利用 G 是树的性质。可以发现 Lk(G) 的一个点对应原图上一个大小不超过 k+1 的连通块,也就是一棵子树。一个显然的结论是两棵子树同构的话,那么它们导出的 Lk(G) 中点数相同。所以可以考虑枚举所有大小不超过 k+1 且互不同构的无根树 T,求得它在 Lk(T) 对应的点数 wT ,在算出它们在 G 中的出现次数 cT ,那么答案即是:

    TwTcT

    k=9 的时候 Lk(T) 的大小早已不能接受,于是结合上面的算法,令 k=k4 ,枚举所有大小不超过 k 的子树,算出 Lk(T) ,再求 L4(Lk(T)) 即可。但是这样算出来的点数可能包含了 T 的其他子树形成的点,所以还要容斥一下。

    至于如何枚举互不同构的无根树 T ,枚举括号序列再树哈希去重即可。

    再考虑计算 cT ,也就是无根树 T 在无根树 G 中的出现次数。直接做是困难的,考虑先随便给 G 指定一个根,这样 TG 中的一次出现里也会有一个点是根,于是便枚举 T 的每个节点作为根,得到 |T| 棵有根树,问题转化为了有根树 T 在有根树 G 的出现次数。

    树上状压 dp : fu,i 表示在 G 上以节点 u 为根,嵌入第 iT 的方案数。由于点是无标号的,所以还要考虑 T 子树之间同构的问题。把所有同构的子树放在一起,转移时再状压一下:gv,S 表示遍历到 u 的儿子 vST 的子树里已经嵌入了的方案数。再加上各种剪枝即可通过此题。

  • 历史

    先考虑如何最大化一个点 u 的灾难度。只有当相邻两次经过 u 的战争来自不同子树(把 u 也看成一棵子树),才能算上贡献。那么设第 i 棵子树内崛起的次数和为 cic0=au ,那么就有两种情况:除了第一次崛起外,其他崛起都在 u 发动战争;某棵子树内崛起次数过多,只能让其他子树满足要求。记 x=c,y=maxc,于是答案为 min(x1,2(xy)) 。容易发现这个结论对所有点都是成立的,且互不影响。于是当 2yx+1 时,答案为 2(xy) ,否则为 y1 。显然一个点 u 至多存在一个子树使得 2cix+1 ,所以不妨令满足这个条件的点对应的边为实边。类似树链剖分,从一个点到根的路径上至多有 O(loga) 条实链和虚边。显然一次修改操作只会影响它到根路径上虚边的变化,所以可以用树链剖分加线段树二分或者魔改 LCT 维护。时间复杂度 O(n+qlog2n)O(n+qlogn)

SDOI2018

“物理实验” 算是道小清新计算几何。“战略游戏” 的做法十分的套路,几乎是模板;“反回文串” 是一道不错的应用于数数的莫比乌斯反演题,细节需要注意;“原题识别” 是一道经典题加强版,提供了在特殊性质下树上链数颜色的方法,其第二问需要大量分类讨论,先解决链上问题是上策;“旧试题” 从题目名字可以看出,是原题的加强版,利用结论和莫比乌斯反演得到一个看似毫无头绪的式子后,可以巧妙地转化到一个图论问题;“荣誉称号” 只要发现了实际的有用的位置很少之后,便是一道很水的 dp 题。

  • 物理实验

    比较友好的计算几何。把挡板投影到导轨上,这样就可以算出来导轨的每一段找到多少长度的挡板。如果算出来了的话,就可以直接双指针所有可能的区间,显然区间的左右端点必然是所有挡板映射到导轨的左右端点之一,否则可以调整,所以有用的区间是 O(n) 个。至于每一段如何计算,考虑扫描线,因为所有挡板以及导轨不交,所以扫描线的过程中线段的相对位置关系没有改变,用一个 set 维护导轨到挡板的 “远近”,计算时二分一下导轨两侧的情况即可。

    显然斜着扫描线很复杂,所以可以先平移坐标系再旋转坐标系使得导轨在 x 轴上。平移坐标系很简单,直接把原向量减去新的原点向量即可。至于旋转坐标系,新坐标系的 x 轴正方向的方向向量是 αy 轴向量方向的方向向量是 β ,那么相当于是对原向量换基,设新向量是 (x,y),原向量是 s ,那么 xα+yβ=s ,解一下二元一次方程组即可。

    时间复杂度为 O(Tnlogn)

  • 战略游戏

    套路题。求出点双连通分量后建出广义圆方树,标记关键点在圆方树上的位置,那么只用数有多少个圆点,使得删掉它之后关键点之间不连通即可。显然是对关键点建出虚树,虚树的大小(只算圆点)减去关键点的个数即是答案。

  • 反回文串

    如果枚举循环节长度直接算的话显然是会算重的,也就是当前的考虑的串可能存在更小的循环节,所以考虑约数容斥,也就是莫比乌斯反演。设 f(n) 为最小循环节长度为 n 的回文串的个数,于是就有:

    d|nf(d)=kd2

    莫比乌斯反演后得到:

    f(n)=d|nμ(nd)kd2

    于是答案为:

    d|ndx|dμ(dx)kx2

    但其实上式并不一定成立,因为当循环节长度是偶数 d 时,循环移动 d2 后又会得到一个回文串,那么均摊下来一个回文串就只会形成 d2 个循环同构,所以真正的答案为:

    d|ndg(d)x|dμ(dx)kx2

    其中 g(d)=[dmod2=0]+1

    交换求和顺序:

    d|nkd2dx|ndμ(x)xg(dx)

    n1018 时,σ(n)103680 ,所以可以 Pollard_Rho 分解质因数之后暴力搜索所有约数进行计算。当搜到的 d 是偶数时,显然 g(dx)=2;否则 g(dx)=g(x) 。求和式的右半部分只有当 μ(x)0 时才有意义,也就是有用的值只有 2ω(nd) 个,可以用高维前缀和分别处理 g(dx)=2g(dx)=g(x) 两种情况的答案。所以枚举到一个约数之后需要的复杂度只是快速幂的 O(logn) 。由于 σ(n)logn>2ω(n)ω(n) ,所以时间复杂度是 O(T(n14logn+σ(n)logn))

  • 原题识别

    以下用 p[u,v] 表示以 u,v 为端点的路径,若为 ( / ) ,则表示不包含 u / v

    先考虑第一问。树上链数颜色目前只能做到 O(nm) 的复杂度,把链拍到欧拉序上莫队即可。此题的树有特殊性质,前一部分是一条链,后一部分是随机父亲的树。一个熟知的结论是随机父亲的树的高度期望是 O(logn) 的,进一步地,发现任意点对 (x,y) (设它们的 lca 是 z)在该树上深度较浅的点(假设是 y),有 depydepz 的期望最大值是 O(logn) 的。证明比较简单,分类讨论即可。于是可以考虑用数据结构计算 p[z,x] 的答案,然后暴力枚举 p(z,y] 上的所有点,单独考虑它们的贡献。

    序列上的区间数颜色问题有熟知的解法:记 preii 左边第一个颜色和它相同的点。那么询问 [l,r] 其实等价于 i=lr[prei<l] ,也就是只考虑区间内每种颜色出现最左边的点的贡献。这是显然是一个二维数点问题,可以用主席树在线解决。上面我们说到,树上链数颜色不能做到 poly log 的复杂度,因为并不能用一个很好的方式定义出每个点的 pre 。但是现在我们的问题是询问一条到根的链的一部分的答案,这个问题就可以类似序列上的问题,在树上建主席树,每个节点的 pre 为它向上第一个颜色与它相同的点。

    下面再考虑 p(z,y] 上点的贡献,也就是 p(z,y] 上有多少颜色没有在 p[z,x] 上出现。枚举 p(z,y] 上的每一个点 u ,为了保证同种颜色不算重,可以用 preuz 的深度关系来保证每种颜色算贡献的是深度最浅的点(也可以用之后的可持久化数组来保证是深度最深的点)。那如何判断 aup[z,x] 上是否出现过呢?考虑用一个可持久化数组记录每个点到根的路径上每种颜色的上一次出现位置,直接查一下再跟 z 比较深度即可。

    第一问的期望时间复杂度为 O(nlogn+mlog2n)

    再考虑第二问,套路地,把询问转化成每个点的贡献。还是先考虑序列上的问题怎么做:假设当前询问 [1,x][1,y]xy),那么分类讨论不同位置元素 i 的贡献。

    • i(x,y]

      为了不算重,还是要保证贡献到的区间里 i 是该颜色第一个出现的,所以左端点的可能的区间为 (prei,x] ,而右端点就只需要满足把 i 包住即可,即 [i,y] ,所以这部分的贡献为 i=x+1ymax(xprei,0)(yi+1)=i=x+1y[xprei](xprei)(yi+1) ,还是一个二维数点问题,只不过信息维护四个:(1,i,prei,iprei)

    • i[1,x]

      当左端点 [1,x] 时,和上面类似,只需要左端点在 (prei,i] 内,右端点在 [i,y] 内;当左端点 [1,y] 时是类似的。所以这部分的贡献为 i=xy(iprei)(x+y2i+2) 。只需要预处理 ipreii(iprei) 的前缀和即可。

    现在再考虑树上怎么做。类似第一问的思路,尝试把序列上主席树换成树上主席树。这样做可以处理 xy 的祖先的情况,也即要把 p[1,x] 拆成 p[1,z]p(z,x] ,把 p[1,y] 拆成 p[1,z]p(z,y] ,分别算每个点对四种情况的贡献。记 q(p[a,b],p[c,d]) 为所有 xp[a,b],yp[c,d](x,y) 第一问的答案和。可以发现 q(p[1,z],p(z,y])+q(p[1,z],p(z,x])+q(p[1,z],p[1,z])=q(p[1,x],p[1,z])+q(p[1,y],p[1,z])q(p[1,z],p[1,z]) 这部分可以直接用序列上的方法解决。

    现在处理 q(p(z,x],p(z,y]) 。类似第一问,先用数据结构计算 ip(z,x] 的答案,再考虑 p(z,y] 会产生哪些额外的贡献,注意到当前考虑到的第一问的询问都会经过 z ,我们直接默认 z 的颜色的贡献都是 z 带来的,即答案直接加上 (depxdepz)(depydepz)

    • ip(z,x]

      类似序列上问题的第一种情况,直接写式子: ip(z,x][depprei<depz](depydepz)(depxdepi+1) 。同样拆开后用之前主席树上维护的信息计算即可。

    • ip(z,y]

      还是暴力枚举上面的每一个点 i,为了不算重,保证它是它的颜色中在 p[z,y] 里最浅的,即 depprei<depz 。于是 p[1,y] 这边可选的端点范围就是 p[i,y] 。而 p[1,x] 那边最远能延伸到的显然是从 z 往下第一个颜色和 i 一样的位置,记为 t 。在第一问用到的可持久化数组上查一下 x 向上第一个和 i 颜色相同的点,如果不在 p[z,x] 上,那么说明不存在 t ,这种情况的答案就是 (depydepi+1)(depxdepz);如果存在,就倍增地跳 pre ,直到刚好要跳出 z 时停止,当前的位置便是 t ,这部分的答案就是 (depydepi+1)(depxdept1) 。当然,此题的颜色也是随机的,所以每种颜色期望出现 O(1) 次,暴力往上跳也是可以的。

    至此,此题全部解决。期望时间复杂度为 O(nlogn+mlog2n)

  • 旧试题

    一个经典的结论是 σ(ij)=x|iy|i[gcd(x,y)=1] ,证明的话考虑其中一个质因子 p ,其在 i 中的次数为 c ,在 j 中的次数为 d ,那么在枚举 xy 时,会对应有一个 p 的次数对 (u,v) ,当 gcd(x,y)=1 时,min(u,v)=0,那么正好有 c+d+1 对这样的 (u,v) 。把每一个 (u,v) 映射到 0c+d 的一个数上,这样对于所有 (x,y) 满足 gcd(x,y)=1 ,都能唯一映射到一个 ij 的因子,故上式成立。

    当有三个数的时候类似,不过是形如三个次数中至少有两个是 0 ,最后的结果应该是:σ(ijk)=x|iy|iz|i[gcd(x,y)=1][gcd(x,z)=1][gcd(y,z)=1]

    带入要求的式子中:

    i=1Aj=1Bk=1Cx|iy|jz|k[gcd(x,y)=1][gcd(x,z)=1][gcd(y,z)=1]

    枚举 x,y,z ,那么合法的 i,j,k 容易直接算出:

    x=1Ay=1Bz=1CAxByCz[gcd(x,y)=1][gcd(x,z)=1][gcd(y,z)=1]

    莫比乌斯反演,并交换求和符号:

    x=1Ay=1Bz=1CAxByCzd1|x,d1|yd2|x,d2|zd3|y,d3|zμ(d1)μ(d2)μ(d3)

    d1d2d3μ(d1)μ(d2)μ(d3)(lcm(d1,d2)|xAAx)(lcm(d1,d3)|yBBy)(lcm(d2,d3)|zCCz)

    显然只有当 lcm(d1,d2)A,lcm(d1,d3)B,lcm(d2,d3)Cμ(d1)μ(d2)μ(d3)0 时,求和的内容才有意义。记 f(n,m)=m|xnnx ,可以 O(NlogN) 求出所有需要的 f ,其中 N=max{A,B,C}

    所以上式可以写成:

    d1d2d3μ(d1)μ(d2)μ(d3)f(A,lcm(d1,d2))f(B,lcm(d1,d3)f(C,lcm(d2,d3)))

    考虑建立一个图论模型,每个数是一个点,两点 u,v 之间右边当且仅当 μ(u)0,μ(v)0,lcm(u,v)M 。于是可以枚举 gcd(u,v) 再枚举 ugcd(u,v)vgcd(u,v) ,搜出来可以发现最多只有 760741 条边。而上式中的贡献是有三种形式:一个点自身算三次,一条边和边连接的点,一个三元环。前两种都很好计算,第三种直接套用无向图三元环计数的方法,度数大的点向度数小的点连边,统计每个三元环的答案即可。

  • 荣誉称号

    可以发现 ax=ax2k+1(x2k+1) ,于是有用的位置就只有 2k+11 个。对 ii2 连边,可以得到一棵完全二叉树。题目的要求其实就等价于根到任意叶子的路径上 a 的和是 m 的倍数。所以预处理一下每个点 x 上所有的 a 变成 y 的代价,然后从叶子开始向上 dp 即可。时间复杂度 O(T(n+m22k))

JSOI2018

“潜入行动” 是一道经典树上 dp 的融合,比较简单,但要会证明复杂度;“防御网络” 的思路很自然,注意对仙人掌的处理即可;“绝地反击” 非常神奇地把计算几何,二分图完美匹配,线段树这三个看似完全不相关的东西结合在一起,是一道不错的题目;“战争” 常被看做是闵可夫斯基和的板子题;“机器人” 深入需要挖掘许多结论,最后的 dp 便显得比较容易;“列队” 是比较基础的主席树以及其上二分的应用题。

  • 潜入行动

    比较简单的树形背包,fu,k,p,q 表示 u 子树内安装了 k 个监听设备,除 u 以外的点都被监听,u 的监听情况是 p ,安装监听设备情况为 q 的方案数。转移时枚举儿子 v 的监听情况和安装设备情况,并判断转移是否合法即可。每个节点有用的 dp 值个数是 O(min(sz,k)) 的,经典结论,总复杂度是 O(nk) 。证明的话考虑合并两个子树的答案的时候对时间复杂度产生贡献的相当于是前一个子树在 dfs 序上的后 min(sz,k) 个点和后一个子树在 dfs 序上的前 min(sz,k) 个点,所以会产生贡献的点对的距离不超过 2k ,比较容易观察出来每个点对是不会重复产生贡献的。

  • 防御网络

    显然不可能对每个点集分别求,考虑每条边的贡献。如果是割边,只要两端都有点,那么这条边一定会被经过,假设一侧的大小是 s ,那么贡献就是 (2s1)(2ns1) 。如果是环边,容易发现一个环上被经过的边一定是一段,大致的形态是如果环上某个点连出去的子仙人掌上有标记的点,那么给环上对应点打标记,然后用最少的边连通环上标记的点。于是考虑对每个环单独处理,容斥,枚举长度 L 表示任意两个标记点的距离不超过 L ,计算出其方案 fLL(fLfL1) 即是该长度的贡献。至于 fL 怎么计算,可以破环链,枚举第一个标记的点,然后 dp ,用前缀和优化一下即可做到时间复杂度 O(n3)

  • 绝地反击

    显然可以二分答案 x ,于是每个点会产生一个可以到达的圆,与攻击轨道相交的部分是一段圆弧。容易发现如果存在解,总存在一种方案使得某个飞船最后的位置是在某个圆弧的一个端点,可以用调整法证明。所以对每个端点判断一次,这样又直接把问题转化成了每个飞船连接环上一段连续区间的点(离散的),问是否有完美匹配。直接 Dinic 的话时间复杂度是 O(n3.5logV) 的,这样的暴力抛弃了题目给的诸多优秀性质。考虑使用 Hall 定理判断。显然不能直接枚举每个左部集合 S ,于是可能会往排序后判断所有连续区间,但这样做正确性是不对的。不妨倒着考虑,枚举右部集合的连续区间,数有多少个左部区间完全被包含在其中,假设右部区间长度为 a ,左部有 b 个区间满足要求,那么只要 ab<0 就说明不合法,否则一定合法。为什么只用判断右部集合的连续区间呢?因为如果右部的某个不合法集合是断开的,那么其中至少也有一个连续区间满足 ab<0 ,可以用反证法轻松证明。破环成链后直接枚举右端点,用线段树维护所有左端点的 ab ,如果某个长度小于等于 n 的区间的最小值小于 0 ,即说明不合法。时间复杂度 O(n2lognlogV)

  • 战争

    如果存在迁徙后两个部落重合,设重合点的原向量分别是 aAbB ,假设平移向量为 w ,那么 b+w=a ,即 w=ab ,于是构造闵可夫斯基和 C=AB ,只需要判断 w 是否在 C 中即可。把 C 的一个顶点固定到原点后,连接原点和其他顶点形成若干条射线,特判掉边界情况后二分查找 w 在哪两条射线中间,然后判断对应的边和 w 的位置关系即可,时间复杂度 O((n+q)logn)

  • 机器人

    显然需要先找到一条路径合法的充要条件,不妨先观察一些性质。

    结论 1 : 处于同一条副对角线上的格子移动方向相同,这里的副对角线是模意义下的。

    这是一个必要条件,因为如果 (x+1,y)(x,y+1) 方向不同那么要么 (x+1,y+1) 会经过两次,要么一次都不能经过。

    结论 2 : 操作序列存在循环节 gcd(n,m)

    这个结论可以通过第一个结论推出来,因为距离为 n 和 距离为 m 的副对角线都是一样的,所以根据裴蜀定理,距离为 gcd(n,m) 的副对角线也是一样的,而每次操作都会从一条副对角线转移到另一条,所以存在循环节长度为 gcd(n,m)

    结论 3 : 令 d=gcd(n,m) ,假设循环节内有 dx 步向下走, dy=ddx 步向右走,那么一条路线合法的充要条件是 gcd(dx,n)=1,gcd(dy,m)=1

    考虑每 ngcd(n,dx) 个循环节回到第一行,每 mgcd(m,dy) 个循环节回到第一列,那么根据第一次回到 (1,1) 的时间,可以得到:

    lcm(ngcd(n,dx),mgcd(m,dy))=lcm(n,m)

    gcd(n,dx)1n=pqn,dx=pqx(p>1) 。那么如果 pm ,那么显然上式不成立,所以可以把 m 写成 m=pqm ,于是 d 也可以写成 pqd ,那么 dy=ddx=p(qxqd) ,于是上式左边会把一个因子 p 约掉,假设不成立。所以得到 gcd(n,dx)=1 ,同理得到 gcd(m,dy)=1

    满足这个条件之后一定会保证经过每一个点吗?根据鸽巢原理,只需要证明不会经过一个点两次即可,而如果存在一个点经过了两次,那么必然会回到 (1,1) 两次,与上面的假设不符。所以每个点都会被经过一次。

    有了结论 3 ,便可以开始考虑每个障碍的贡献。先枚举 dx ,这样从起点开始的每 d 步走到的位置是确定的。假设在第 t 个循环第一次遇到障碍,那么需要满足之前都不经过障碍。那么可以把前 t 个循环可能走到的点取出来叠加,形成一个新图 Gt ,在 Gt 上刚好走到 (x,y) 会遇到障碍,在 Gt1 上能够经过 (x,y) 走到 (dx,dy) 。于是开两个 dp 数组,分别在 Gt1 上 dp 从 (dx,dy) 不经过障碍走到 (x,y) 的方案数和在 Gt 上 dp 从 (0,0) 不经过障碍走到 (x,y) 的方案数,乘法原理乘一下在算上路径长度即可。

    时间复杂度 O(Tn4) ,但跑不满。

  • 列队

    比较显然,必然存在一种最优方案使得坐标小于等于 x 的人往右走,大于 x 的人往左走,这个 x 是容易在主席树上二分出来的,也可以直接在二分的过程中计算答案,时间复杂度 O((n+m)logV)

BJOI2018

“求和” 考察选手是否会写代码;“二进制” 的结论不难发现,但线段树维护的信息过多,需要整理好思路;“染色” 是个神仙结论题,有时间再看;“治疗之雨” 的列期望方程组比较套路,使用特殊的消元方法便能解决;“链上二次求和” 需要一步步把问题转化,从一阶前缀和逐渐推理到四阶前缀和,然后用维护高阶前缀和的通法维护即可;“双人猜数游戏” 是一道非常神仙的逻辑推理游戏,虽然最重要的逻辑只有一个,但是彻底想清楚不容易。

  • 求和

    对每个 k 维护每个点到根路径的答案和,查询时用 lca 把路径拆成四条到根的路径即可。

  • 二进制

    容易发现当 1 的个数是偶数的时候一定合法。当 1 的个数是奇数的时候最优策略是把 1 排成形如 111...1010100...000 ,只有当区间内 1 的个数为 1 或者区间内 0 的个数小于 2 的时候会不合法。不合法的区间显然比合法的好统计,考虑用线段树维护一段区间内不合法区间的个数,只要能实现区间合并即可。

    1 的个数为 1 的情况分类讨论 1 在左区间还是右区间,以左区间为例,计算右区间的前缀 0 的个数,再计算一下左区间后缀第一个 1 左边有多少个 0 ,乘一下即可;区间内所有数都是 1 的情况直接算一下左区间后缀 1 的个数和右区间前缀 1 的个数,分奇偶讨论一下即可;区间内只有一个数是 0 的情况类似 1 的个数是 1 的情况,不过也要分奇偶讨论。

    但这些情况里有两种特殊情况会算重,一个是长度为 1 且是 1 的区间,这个可以直接在赋初值的时候处理;一个是长度为 20110 ,这个可以在合并的时候判断左区间最后一个数和右区间第一个数然后减掉。

    时间复杂度 O(n+mlogn)

  • 染色

    结论题,先把 Alex_Wei 的题解 挂着,有空再看。

  • 治疗之雨

    容易预处理出 Pi=(ki)mki(m+1)k 表示某一轮被恰好打了 i 次的概率,k 可能很大,需要把组合数拆开算。设 Ei 表示从血量为 i 被打死的期望步数,E0=0 。分类讨论一下有没有被治疗到便可得到 n 个关于 E1,E2,,En 的方程(写式子的时候不必在意某一轮已经被打死了但还在被打的情况,因为这个系数贡献给的是 E0=0)。直接朴素高斯消元就可以做到 O(Tn3) 。发现可以以 E1 为主元,主元法消元,时间复杂度 O(Tn2) 。注意特判无解和边界即可。

  • 链上二次求和

    对于询问,拆成两个前缀相减,也就是维护答案的前缀和数组(答案数组 a 指节点个数等于 x 的路径权值和为 ax)。先考虑单点修改的情况,假设修改位置 i,修改增量为 v ,那么对 ax 的影响便是 (min(nx+1,i)max(ix,0))v ,把其看成关于 x 的函数,大概是前面一段斜率为 v ,中间一段斜率为 0 (可能没有),后面一段斜率为 v 的分段函数。可以看作是对 a 的二阶差分进行常数次单点修改,具体地,就是:

    Δ2a1Δ2a1+v

    Δ2ai+1Δ2ai+1v

    Δ2ani+2Δ2ani+2v

    于是用树状数组维护 Δ2a 的三阶前缀和即可得到 a 的前缀和数组。

    再考虑区间修改,相当于是在 a 的二阶差分上进行区间加操作,于是也就是在 a 的三阶差分上进行常数次单点修改,如法炮制,维护 Δ3a 的四阶前缀和即可。

    对于 k 阶前缀和的维护,假设要算 a 数组第 n 个位置的结果,设 G(z)a 的 OGF,那么其 k 阶前缀和的 OGF 为 G(z)1(1z)k,广义二项式定理展开即可得到结果是:

    i=0n(ni+k1k1)ai

    n 确定时,是一个关于 ik1 次多项式,用树状数组维护 itat,t[0,k) 的前缀和,并暴力预处理每个 n 的多项式系数即可。时间复杂度 O(nk2+qklogn) ,在此题中可以认为时间复杂度为 O(n+qlogn)

  • 双人猜数游戏

    通过手玩样例可以发现,双方是通过不断地假设来排除答案,当某一时刻剩下的答案唯一时,便知道答案。于是考虑 dp :ft,i,j 表示双方一共说了 t 次 “不知道” 后,如果答案是 jk ,下一个回答主持人的人是否能够知道答案。显然,如果他上一次回答的时候就知道,那么这一轮也知道,所以有:ft,i,jft2,i,j。然后,以 Bob 为例,如果对于 t[s,i+j2]/{i},fi1,t,i+jt=1ft1,i,j=0 ,那么根据上一轮 Alice 说 “不知道”,可以推出答案一定是 (i,j) ,即令 ft,i,j=1 。这样可以一直推到第一个人说知道,但是有一个问题是有可能一个人(不妨假设是 Alice)知道了,另一个人还是不知道,因为有可能从 Bob 的视角看,上一次 Alice 说知道的时候有多种可能,也就是 Alice 在上一次 dp 转移时第二种转移对于些 i+j=c 的位置赋值了多次,而 Bob 只知道 c 是不能确定到底是哪个 (i,j) 是答案的。所以说,最后输出答案的时候还要判断一下另一个人能否知道答案(因为题目说了最后两个人都知道答案了),也即上一次转移的时候是否只有一组 i+j=c 被赋值。打表可以发现答案不超过 300 ,直接 dp 即可。

八省联考 2018

“劈配” 是一道有趣的题,转化到图论问题后就各显神通;“林克卡特树” 的暴力树上背包做法不难想到,跟凸性有关的题做得比较多的话也容易观察出此题的凸性;“制胡窜” 的思路并不困难,但是推式子和分类讨论比较麻烦,由于我是之前做的,就不再写一遍讨论过程,贴个别人题解。

  • 劈配

    容易建出一个图论模型:一张二分图,左部点是选手,右部点是导师,选手只能匹配一个导师,但导师可以匹配 bi 位选手,每个选手的志愿是一组互不相交的边集。对于第一问,直接使用匈牙利算法或者 Dinic ,每一次枚举当前选手的志愿,以当前志愿为出边尝试增广,如果导师没满,那就匹配成功,否则枚举导师更换哪名选手。如果某次志愿增广成功,直接停止即可。单次增广时每个选手(除去当前枚举的选手)及其志愿最多被访问一次,时间复杂度 O(n2C)

    对于第二问,显然可以二分答案,但每次重新增广的话时间复杂度是 O(n3Clogn) 的。注意到其实可以在做第一问时把每一轮增广的结果存下来,这样子判断的时候就只用增广一轮,时间复杂度 O(n2Clogn)

  • 林克卡特树

    边权为 0 的边并没有作用,所以可以把问题转化成选 k+1 条不相交的链,使得收益最大。朴素 dp 的话直接树上背包:fu,i,0/1/2 表示 u 子树内一共选了 i 条链,当前 u 的度数是 0/1/2 的最大收益。合并两个子树时分类讨论一下即可,注意可能选单点,所以赋初值时 fu,1,2=0 。时间复杂度 O(nk)

    一般这类树上 dp 问题都有凸性,此题也不例外,可以从两个方面感性理解:转移式子整体类似 (max,+) 卷积,也就是闵可夫斯基和;当选的链逐渐增多时,权值是不断增加的,加到一定程度之后就不得不抛弃一些边。当然两个方面都不严谨,具体的证明我并不会,但不影响做题。考虑 wqs 二分,二分斜率为 k 的直线去切凸函数 F(x),令 Gk(x)=F(x)kx,则显然 Gk(x) 的最大值点即是切点,根据切点横坐标来调整斜率。把 dp 稍微改一下,存一下当前最优策略下选了几条链,以及产生或者合并链的时候算一下 kx 的贡献即可。时间复杂度 O(nlogV)

  • 制胡窜

    先建出 SAM , 然后可持久化线段树合并维护每个节点的 endpos 集合,查询时先倍增到对应节点上,然后就是分类讨论。大致的思路是用区间总数减去不合法的区间数量,然后观察到如果存在三个互不相交的区间,那么一定合法。于是就讨论最左边出现的子串和最右边出现的子串是否相交,把 i 的位置分情况讨论可以得到一些式子,最后的信息都可以在线段树上维护,具体的讨论过程 。时间复杂度 O((n+q)logn)

九省联考 2018

“一双木棋 chess” 是一道不难的博弈论 dp 题;“IIIDX” 的贪心并不难想,但是线段树维护的 f 的方法及其正确性需仔细思考;“秘密袭击 coat” 是一道比较神仙的题,但每一部分又都感觉很套路。

  • 一双木棋 chess

    容易发现任何时刻可以落子的地方是一条只能向右或向上走的折线挨着的格子,这样的折线显然只有 (n+mn) 个,于是记忆化搜索局面是某条折线时当前得分差值最大是多少,暴力枚举转移即可,时间复杂度 O((n+m)(n+mn))

  • IIIDX

    容易想到一个贪心:将权值从小到大排序后,给当前点 u 最小的权值,然后依次给它的每一个子树分配最大的一段,次大的一段... 但是这样做只能解决 di 互不相同的情况,存在 di 相同时,可以让 u 的儿子 v 子树里的权值尽量和 v 一样,这样就能留出一些更大的权值去给 v 的兄弟,因为深度越浅编号越小。但还是可以贪心,按编号从小到大,给一个点 u 选好权值之后,它子树里的点的权值都必须大于等于它,于是我们可以考虑记录一个 fi 表示当前能选多少个权值大于等于 i 的数。假入给 u 的权值是 xu 的子树大小是 su ,那么 x 的值一共会被占用 su 个,所以 f1fx 一定都会减去 su ,但是剩下的 f 呢?这是不确定的,所以我们并不能真正的维护 f ,但是显然 f 是单调递减的,我们维护的 f 的前缀 min 和我们假想的 f 是一样的,因为你并不知道权值大于 x 的数会被怎么占用,但你只需要留出来这么多,就一定合法。

    所以用线段树实现区间减,查询时线段树上二分最大的 x 使得 mini=1xfisu 即可。注意到一个点的时候要把父亲给它占用的位置还回来,并且每个父亲只能还一次。时间复杂度 O(nlogn)

  • 秘密袭击 coat

    关于第 k 大的计数,可以套路地转化:

    i=1wiS[Sk=i]=i=1wiS([Ski][Sk>i])=i=1wS[Ski]=i=1wS[cS,ik]

    其中,Sk 是连通块 S 的第 k 大,cS,i 是连通块 S 里大于等于 i 的数的个数。于是可以对着最后的式子做树上背包:fu,i,j 表示以 u 为根的连通块,大于等于 i 的权值出现了 j 次的方案数。直接树上背包合并即可,时间复杂度 O(n2w)

    考虑到树上背包实际上是卷积,考虑用点值表示加速转移。设 Fu,i(z)=jfu,i,jzj ,那么转移就是:

    Fu,i(z)Fu,i(z)(Fv,i(z)+1)

    初值的话,如果 dui ,那么 Fu,i(z)=z ,否则 Fu,i(z)=1 。因为最后要求的是 uij=knfu,i,j ,所以为了方便统计答案,再算一下 Gu,i(z)=Fu,i(z)+vson(u)Gv,i(z) 。由于最后的 G1,i(z)n 次多项式,所以带 n+1 个点值进去计算,再拉格朗日插值即可,但时间复杂度还是 O(n2w)

    考虑到 i 这一维只影响初值,不影响转移,尝试使用线段树合并同时进行所有 i 的转移。对于 FG 的操作只有乘法和加法,所以都可以把转移写成矩阵的形式。赋初值时两次区间修改即可;线段树合并时,如果待合并两个节点中有一个没有儿子,那么说明它区间内的 FG 都是一样的,所以可以直接把它的贡献乘到另一个节点上。至于 Fv,i(z)+1 可以在 Fv,i(z) 计算完毕的时候处理。对于这种线段树合并的复杂度证明:在没有合并操作时,所有线段树的节点总数是 O(nlogw) 的。进行合并操作时,Push_Down 操作时原先的节点一定是有儿子的,所以不会新产生节点,而每进入一次 Merge 函数,线段树的节点总数就会减 1 ,所以单次 dfs 线段树合并的时间复杂度是 O(nlogw) 的。因为要带 n+1 个点值,所以总时间复杂度是 O(n2logw)

APIO2018

“铁人两项” 是一道比较套路的题,注意分类讨论时算清楚即可;“选圆圈” 是一道正解很优雅,但暴力碾标算的题;“新家” 的三 log 做法并不难想,利用此题的特殊条件便可优化到单 log

  • 铁人两项

    由于每个点只能经过一次,所以套路地,缩点双建出广义圆方树,显然枚举 c 更方便计数。然后分类讨论一下 p(c,s)p(c,f) 是否都经过了 c 所在点双的非 c 点。如果存在一条路径没有经过,那么相当于是在圆方树上 c 的两个不同子树(包括父亲子树)内各选一个点,那么就用随便选的方案减去选在同一个子树内的方案,维护圆方树上的子树大小计科;如果都经过了,那么可以相当于在 c 对应的方点的各个子树中选两个来自不同子树的点,且这两个点都不在 c 的子树里,同样可以用随便选的方案减去选在同一个子树内的方案。为了保证复杂度,可以枚举方点,然后把方点连出去的所有点的贡献归并在一起算。上述两种情况以更形象的表述可以分别理解为 c 是割点且两条路径经过不同点双和两条路径经过了同一个点双(c 所在的那个)。注意图不一定连通即可,时间复杂度 O(n+m)

  • 选圆圈

    直接去找一个圆会删掉哪些圆不太方便,但是找是哪个圆删掉它的要相对容易些,因为它是唯一的。假设已经确定了 S 里的所有圆是被自己删掉的,那么显然它们互不相交(这里的相交是题目中定义的相交),当前考虑被删圆 O0 的半径不大于 S 里任何圆的半径,那么可以得到一个结论:如果 S 里的某个圆 OO0 相交,那么必然与 x=xOrO,x=xO+rO,y=yOrO,y=yO+rO 中的一条直线相交。证明的话考虑反证,那么 O 可能存在的区域就有九个,但显然这九个都不可能。有了这些之后,我们考虑从左到右扫描线,以圆的纵坐标为关键字,逐渐把 S 里的圆插入到 set 里或从 set 中删除,因为 S 中的圆是互不相交的,所以相对顺序不会发生变化。查询时在 x=xO0rO0x=xO0+rO0 处查询前驱后继,判断其相交情况并取较优答案即可,这显然是对的。但是这只处理了两条直线,所以还要从下往上扫描线一次处理另外两条直线的情况。

    那如何解决原问题呢?其实可以发现这个问题类似 “前面的修改对后面会产生影响”,自然地,我们想到用 cdq 分治解决这个问题。先递归解决左区间的问题后,左区间 S 里的圆就确定了,那么对这些圆做两遍扫描线,并尝试更新右区间圆的答案,再递归到右区间即可。时间复杂度 O(nlog2n)

    当然,此题可以把圆想象成一个正方形后在 KD-Tree 上爆搜剪枝,比上述做法快不少。

  • 新家

    先考虑某一时刻的查询怎么做。注意到求的实际是所有种类的距离最近商店中距离最远的,可以二分答案,于是转化为了判定区间内是否所有颜色都出现过。区间数颜色可以维护每个位置的前驱指针来转化成二维数点问题,加上修改,就变成了三维数点问题,但是外面还有一层二分,复杂度不太能够接受。但考虑到此题的判定只需要确定是否所有颜色都出现,那么可以发现,如果在序列的最左端对每种商店插入一个哨兵商店,那么查询区间 [r+1,n] 的前驱指针的最小值是否大于等于 l 即可。这件事情还可以在线段树上直接二分,所以时间复杂度降到了 O((n+q)logV)。实现的时候注意一个位置可能有多个商店,需要每个位置额外开一个 multiset 维护。

WC2018

“通道” 题意简洁,但是一步一步把每棵树的影响去掉需要对图论的各个算法有较深理解和熟练掌握,实现起来虽然全是板子,但是还是值得一做;“州区划分” 只要会子集卷积就不难;“即时战略” 是道神仙交互题,通过部分分可以想到正解的思路,点分树重构部分类似于 “紫荆花之恋”。

  • 通道

    先考虑用边分治消除第一棵树的影响,即假设当前分治中心左右两边的点集分别为 L,R ,两边点到分治中心的距离分别是 dL(u)dR(u) ,那么现在只需要从 LR 里各选一个点 i,j ,最大化 dL(i)+dR(j)+d2(i,j)+d3(i,j) 。考虑在第二棵树上建出 LR 的虚树,其点权是第二棵树上到根的路径和,加上 dL(u)/dR(u) ,这样的话,只需要枚举第二棵树上的 lca ,然后问题就转化成了在 lca 的子树中分别选一个 LR 里的点,使得 d3(i,j)+w(i)+w(j) 最大,其中 w(i) 是指 i 在第二棵树上点权。这个东西可以理解成在第三棵树上每个节点 i 连了一条 (i,i,w(i)) 的边,两点 (i,j) 的距离变成了 (i,j) 的距离,因此还是满足直径的性质。因此可以在第二棵树上依次计算每个点 u 子树内 L 集合里的点的直径,R 集合里的点的直径,在合并子树的同时统计答案即可。直接实现的时间复杂度是 O(nlog2n) 的,可以使用一些技巧优化至 O(nlogn)

  • 州区划分

    可以直接枚举点集,然后用并查集和度数判断是否存在欧拉回路,然后题目要求的式子可以直接 O(3n) 枚举子集 dp ,但很容易就发现那个子集 dp 是一个半在线子集卷积,那就按元素个数依次做子集卷积就好了,时间复杂度 O(2nn2)

  • 即时战略

    链部分的限制更紧,不妨先考虑链的做法。题目给的次数大致要求我们只允许 logn 次出错。一个简单的想法是假设当前确定了链上的 [l,r] ,每次随便找一个点 x,如果 explore(l,x) 没有访问过,那就一直向左走,把链扩张到 [x,r] ,否则就向右走,把链扩张到 [l,x] 。期望要找 logn 次,每一次错误的概率是 12 ,所以出错次数可以接受。

    对于其他部分,容易想到一个暴力:每次找一个未探索的点,不断向它走,操作次数是起点和终点的深度之和,可以通过完全二叉树的部分。这启发我们只要把每次走的步数控制在 O(logn) 以内,就可以通过此题。这个结构和点分树很类似,不妨考虑在点分树上解决这个问题。我们把起点设为点分树的根 rt,然后开始寻找下一个点 x 在原树上深度最浅且已经被发现的祖先。我们找到 explore(rt,x) ,并不断在点分树上跳父亲,这样可以找到 explore(rt,x)rt 的点分树上哪棵子树,再以这个子树的根为新的 rt ,不断找即可。由于要动态加点,所以类似替罪羊树,当某个节点满足 αszfax<szx 时,就重构以 fax 为根的点分树,α 我取的 0.7 。时间复杂度 O(nlog2n) ,查询次数 O(nlogn)

NOI2018

“归程” 的思路简单,做法比较套路;“冒泡排序” 需要大胆猜结论,之后的 dp 以及其组合意义的转化比较厉害;“你的名字” 对于熟练 SAM 的人来说不算难题。“屠龙勇士” 大概是 exCRT 的板子题;“情报中心” 是一类很套路的题,但往往答案在何处计算,信息的维护以及细节的处理对思维要求很高;“多边形” 是一道毒瘤状压 dp 题,细节我还不会,建议用来欣赏。

  • 归程

    先考虑离线怎么做。把询问按照水位线排序,这样子每次能开车走的边集是不断在之前的基础上向里面加边,于是可以用并查集维护开车能互相到达的连通块,从连通块里选一个点走回家,那么只需要一开始最短路预处理一下,取连通块里最小的点即可。

    强制在线的话只需要把每个时刻的并查集的形态维护出来即可,可以建出 Kruskal 重构树后查询在树上倍增,也可以把并查集换成可持久化并查集。

  • 冒泡排序

    根据样例解释,可以大胆猜测一个排列是好的,当且仅当不存在 i<j<k ,使得 pi>pj>pk 。简单证明一下:如果存在这种情况,那么 pjpi 比较时就会往左移,和 pk 比较时就会往右移,发生了折返,显然不是最短步数;如果不存在这种情况,那么一个数如果左边有数比它大,那么右边就不会有数比它小,它就会一直往左移,另一个方向同理。

    有了这个结论,先考虑没有字典序限制时怎么做。可以发现,从左到右填数时,下一个填的数要么是前缀最大值,要么是之前没有出现过的数的最小值,不然一定不合法。所以考虑 dp :fi,j 表示前 i 个数填好了,它们的最大值是 j ,后 ni 个数有多少种填法。于是转移就是讨论第 i+1 个数是否是前缀最大值,得到转移方程:

    fi,j=k=jnfi+1,k

    注意,根据定义 i>jfi,j=0 。考虑 fi,j 的组合意义:从 (i,j) 出发,每次向上走若干步,向右走一步,不经过直线 y=x1 ,走到 (n,n) 的方案数。可以发现走的方式等价于只能向上走和向右走,所以直接用求卡特兰数的翻折法即可得到:

    fi,j=(2nijni)(2nijni+1)

    再考虑有字典序的限制怎么做,我们关心前 i1 个数与 q 相同,第 i 个数严格大于 qi 的合法排列有多少个即可。如果前 i1 个数已经不合法那么就显然之后的贡献都是 0 ,否则假设 x=maxj=1iqj,显然第 i 个数只能填比 x 大的数,那么方案数就是:

    j=x+1nfi,j=fi1,x+1

    时间复杂度 O(Tn)

  • 你的名字

    先考虑 l=1,r=n 时怎么做。考虑枚举 T 的前缀,找到其一个极长的在 S 中没有出现过的后缀,也就是直接在 S 的 SAM 上跑匹配,就能算出 T 中有多少串满足要求。至于要求本质不同的话,可以对 T 也建出 SAM ,每次找到一段不合法的串后,对应了 T 的后缀树某个节点到根的路径上的串都不合法,给那个节点打个标记,最后再一起统计答案即可。

    更一般的情况,发现只有与 S 匹配时有区别,需要多一个某个 T 的子串是否在 S[l:r] 中出现过,那么可持久化线段树合并预处理 S 的 endpos 集合,并在线段树上查找即可。时间复杂度 (O)((|S|+|T|)log|S|+Q)

  • 屠龙勇士

    选剑部分直接用一个 multiset 模拟即可,而对于 x 的计算,列出 n 个同余方程后用 exCRT 合并它们即可。

  • 情报中心

    懒得画图了,直接对着 这篇题解 的图讲。可以发现两条路径的代价是 12(dis(u1,v1)+dis(u2,v2)+dis(u1,u2)+dis(v1,v2))w1w2 ,考虑在 t=lca(u1,u2) 处计算这两条路径的贡献。为了避免分数,计算 ans×2 的最小值,最后除以二即可。 2dist 是定值,disu1+dis(u1,v1)w1 可以看成路径 1 的权值,路径 2 的权值同理,那么还剩下一个 dis(v1,v2) ,把路径 1 的权值放到 v1 上,那么就变成了有点权有边权的直径问题了。用线段树合并和 O(nlogn) 预处理,O(1) 查询 lca 维护一个子树内所有合法路径的 v 形成的直径即可。注意当遍历到 t 时,路径 1 就不会再对之后的路径产生贡献了,所以要在返回 t 之前就从对应线段树中删去。还要注意一下两条路径 u 相等的情况的贡献计算。单组数据时间复杂度 O((n+m)lognm)

CTSC2018

“假面” 套了一个询问和修改,本质是一个比较简单的可撤销背包;“暴力写挂” 和 “通道” 一样是非常经典的题,可以用性质优秀的边分树解决;“青蕈领主” 是一道类似析合树计数的问题,根据析合树的相关知识可以将问题转化,新的问题的递推证明非常巧妙,最后求解的式子是全在线卷积,需要特殊处理;“混合果汁” 只要顺着思路想便容易解决;“字典树” 原题解做法是错误的,真做法我并没有有学会,不过链的部分还是可以想一下;“组合数问题” 是一道非常有趣的提交答案题,考察了多个方面的知识点。

  • 假面

    容易实时维护 dp :fi,j 表示当前第 i 个人有 j 点生命值的概率。至于询问,可以再设计一个 dp : gi 表示恰好有 i 个人存活的概率。计算答案时枚举哪个人被锁定,前提是这个人一定存活,那么相当于还要对 g 做一遍可撤销背包。时间复杂度 O(Qn+Cn2)

  • 暴力写挂

    枚举 lca(x,y) ,这样只需要最大化来自两个不同子树的 x,ydisx+disydislca(x,y) ,这个式子长得很像距离,可以把它变形成 12(disx+disy+dis(x,y)) ,如果只查询一次的话边分治就好了。考虑建出边分树,其结构类似线段树,其合并类似线段树合并。边分树上一个节点代表被边分治划分出来的某一连通块当前点集的信息和,初始时,直接在边分治的同时,向分治中心两侧的连通块分别 dfs ,给经过点的边分树上加一个左儿子/右儿子,并记录 disx+dx 和分治中心的长度,dxx 到分治中心的距离。这样,在 T 就可以实现快速的信息合并,统计答案的话就在边分树合并的时候计算即可。时间复杂度 O(nlogn)

  • 青蕈领主

    根据题目的定义,一个排列会形成若干个极长的连续区间(这和析合树的本原连续段有所不同),根据析合树的知识,可以类比推导出两个极长的连续区间要么不交,要么包含,这样也形成了一个树形结构,容易用一个栈来建出这棵树。

    可以发现的是,对于树上的每个节点,它儿子的权值分配方案是独立的,也就是对于每一个点 u ,假设它有 x 个儿子,它的所有儿子 v 里的值域区间是连续的,把这些区间离散化成 1x 后,此时形成的排列一定满足 Li=1(ix),Lx=x() ,满足 () 的排列总数是 fx1 ,那么 u 就有 fx1 种方式分配 x 个儿子的大小关系,之后每个儿子内部如何分配权值就是一个子问题了。所以答案就是 fx1

    现在计算 fn 。在这之前,先明确一下我们要数的排列的特点 () :它的所有连续区间要么长度为 1 ,要么是一段后缀。

    考虑在长度为 n 的排列中插入 n+1

    • 如果原排列就满足 () ,那么 n+1 只要不和 n 相邻,那么新排列一定满足 ()n1 种插入 n+1 的方式,所以这种情况的方案数是 (n1)fn1

    • 如果原排列不满足 () ,那么要插入 n+1 来破坏一些连续区间。若存在两个及以上不交的长度大于 1 的连续区间,那么显然一定无法插入 n+1 使得排列满足 () 。所以现在原排列满足任意两个长度大于 1 的连续区间满足包含关系。枚举除了 [1,n] 以外的最长的连续区间的长度 i

      • 类似上面对树的分析,把这段区间和 [1,n] 的其他元素离散化,这样形成的排列必须满足 () ,方案数是 fni

      • 再考虑这段区间离散化后的取值,它不能是 ni+1,不然插入 n+1 后这段区间还是连续的,并且它也不能是离散化后最后一个位置的值,不然它是原排列的一段后缀,满足 ()。由于最后一个位置的值不可能是 ni+1 ,要不然除开最后一个位置的整个区间也是一个连续区间,所以能选的取值有 ni1 个,由此可以推出 i[2,n2]

      • 现在就只需要考虑被破坏的这个区间以及插入的方案数。可以发现,插入 n+1 之后,原来这个区间里所有长度大于 1 的连续区间都经过 n+1 所在的位置,这样它们就被破坏了。不妨假设这个区间里的值分别是 1i ,把 n+1 视作 i+1 ,于是新排列的所有长度大于 1 的连续区间都经过 i+1 ,这样的排列数量恰好是 fi。因为 “连续” 这个性质关于下标和值是对称的,那新排列的逆就恰好满足 () 。排列和排列的逆是一一对应的,所以方案数恰好就是 fi

      所以这种情况的方案数是 i=2n2(ni1)fifni=i=2n2(i1)fifni

    递推关系即为:

    fn=(n1)fn1+i=2n2(i1)fifni=(3n)fn1+i=1n1(i1)fifni

    考虑分治 NTT 预处理 f ,设分治的函数为 cdq(l,r)。但是一般的分治 NTT 是形如 fn=i=0n1figni 的形式,这样就可以用 flmid 去卷 g1rl 去贡献给 fmid+1r 。但如果是 ff 的话,就有可能 f1rl 还没有算出来。容易证明这种情况会发生当且仅当 l=1 。考虑在 cdq(1,r) 时对 fn,n[mid+1,r] 的计算做一下变形:

    fn=(3n)fn1+i=1nmid1(i1)fifni+i=nmidmid(i1)fifni+i=mid+1n1(i1)fifni=(3n)fn1+i=1nmid1(i1+ni1)fifni+i=nmidmid(i1)fifni=(3n)fn1+(n2)i=1nmid1fifni+i=nmidmid(i1)fifni

    对于最右边的式子,不管是 fi 还是 fni ,都已经在 cdq(1,r) 执行完 cdq(1,mid) 后算出来了,所以直接把 [1,mid][1,mid] 卷起来贡献给 [mid+1,r] 即可。对于中间的式子,可以发现其跟原来在 cdq(mid+1,r) 时整个递归过程的贡献方式是一模一样的,即当 l1 时,cdq(l,r) 卷的是 flmidf1rl ,并给每一项结果乘一个 n2 ,以计算其原本的贡献并补偿其分治树上第一个 l=1 的祖先区间未计算的部分。

    时间复杂度 O(nlog2n+Tn)

  • 混合果汁

    显然可以二分答案,问题转化为美味度大于等于 x 的果汁能否用不超过 g 的价格凑出 L 升,显然用单价尽量低的果汁即可。于是对果汁按美味度排序后,按 g 建出主席树,查询时外层二分套内层主席树上二分即可,时间复杂度 O(nlogn+mlog2n)

  • 组合数问题

    1. 测试点 1,2

      数据范围很小,暴力枚举所有数据,按照题意模拟,取最优解即可。

    2. 测试点 3

      只有三台机器,没有依赖关系,要求延迟最小。容易转化成给每个问题分配一个机器,三台机器各自的用时总和的最大值的最小值即是答案。容易背包求解:fi,j,k 表示前 i 个问题,第一台机器用时 j,第二台机器用时 k ,第三台机器的最短用时,dp 时记录转移便可输出方案。

    3. 测试点 4

      依赖关系呈三条互不相关的链,要求总时间最小。那么对三条链分别做,fi,j 表示前 i 个问题,当前问题给第 j 台机器的最少总时间,转移枚举下一个问题给哪台机器,同样记录转移即可。

    4. 测试点 5

      和测试点 4 类似,但是依赖关系更加复杂,机器数只有 5 。但可以发现一个问题依赖的问题是一段很小的区间,对数据 check 后发现这个区间长度最大是 5 ,于是和测试点 4 类似,记录最后 5 个问题分别分配给哪些机器即可。

    5. 测试点 6

      依赖关系没有规律,机器数为 2 ,要求总时间最小。考虑两个有依赖关系的问题会发生传输当且仅当机器不同,容易联想到最小割。即 S 向所有点连流量为在第二台机器花费的时间,所有点向 T 连流量为在第一台机器花费的时间,如果存在依赖关系 (x,y) ,那么连一条 x 指向 y ,从第一台机器传输到第二台机器的时间,和一条 y 指向 x ,从第二台机器传输到第一台机器的时间。最终 S 集合里的点即是分配给第一台机器的问题。

    6. 测试点 7

      没有依赖关系,要求计算总延迟。观察到机器比问题多,并且计算时间都在 [1000,2000) 范围内,答案也只是 1014 ,所以每台机器只会解决一个问题。那么把计算时间小于等于 1014 的问题和机器连一条边,跑二分图最大匹配即可。

    7. 测试点 8

      与测试点 7 相比,多了依赖关系。容易观察到这个依赖关系是把除了 1n 的点分成 10 层,每层 50 个点,层与层之间是完全依赖关系,所以可以对每一层跑测试点 7 的算法。但是不知道每一层的答案,每一层计算的时候还要二分答案。

    8. 测试点 9,10

      整体来看没有特殊的规律(测试点 9 的依赖关系有一点性质),数据范围很大,也不支持跑暴力,模拟退火就好了。

PKUSC2018

整体来说都不难,也都不算很简单,除了 “主斗地” 是爆搜以外,其他题都可以看看。

  • 真实排名

    分类讨论每个数 x 是否被翻倍:

    • 没翻倍:那么只能选 [0,x2)[x,+) 里面的数

    • 翻倍了:必须选 [x,2x) 里面的数,其他数无所谓。

    二分出对应区间的数的个数后组合数一下就好了。

  • 最大前缀和

    为了不算重,枚举序列最后一个最大前缀和的集合 S 进行计数。发现 U/S 形成的序列需要保证任意非空前缀和都小于 0 ,可以状压 dp 计算,而 S 形成的序列需要保证 i(i<|S|)sumisum|S| ,等价于任意后缀和大于等于 0 ,也可以状压 dp 计算。时间复杂度 O(2nn)

  • 主斗地

    可能的牌并不多,可以直接爆搜,顺子之类的东西是没用的,因为拆成单张更有利于打完。

  • 星际穿越

    容易发现,起点固定时,步数随终点的减小而减小。值得注意的是,由于此题是连的双向边,所以第一步是有可能往后跳的,如果往后跳,一定是跳到满足 lyxly 最小的点,第二步就会跳到 ly。其实就可以发现,如果步数大于等于 2 ,那么假设当前在 z ,那么下一步跳的位置就是 mini=znli 。所以可以倍增算出从每个位置出发跳 2i 步能跳到的最远位置,顺带的便能算出到这段区间的步数总和,查询的时候转化成后缀和减一下就好了。时间复杂度 O((n+q)logn)

  • 神仙的游戏

    border 的一个熟知的性质是长度为 len 的 border 意味这长度为 nlen 的循环节(可能不能补全整个字符串)。反过来也是成立的,所以不妨考虑循环节。如果 S[i]=00,S[j]=11 ,那么就不可能存在长度为 x|ij| 的循环节。令 fk 表示满足 ij=k,S[i]=00,S[j]=11(i,j) 个数,那么 f 可以差卷积求出,再枚举约数即可。时间复杂度 O(nlogn)

  • PKUSC

    先拆期望,问题转化为每个点在多边形内的概率之和。对每个点单独考虑,多边形的旋转等价于点的旋转,所以相当于是求圆在多边形内的周长。显然当圆与多边形相交时,圆才有可能穿过多边形。于是求出圆与多边形的所有交点,极角排序一下,相邻的两个交点之间的弧上的点要么都在多边形内,要么都在多边形外,随机取上面一个点,用射线法判断即可。时间复杂度 O(nm2)

PKUWC2018

六道模 998244353。 “Minimax” 的 dp 不难,优化算是一个套路;“Slay the Spire” 是一道比较套路,思路自然的数数题;“随机算法” 魔改一下最大独立集就好了;“随机游走” 的几部分做法也都是套路;“猎人杀” 有两种解法,最后都是分治 NTT,其中一种做法的转化比较巧妙;“斗地主” 是爆搜题。

  • Minimax

    容易想到一个 dp :fu,i 表示节点 u 取到第 i 小的权值的概率。转移只讨论有两个儿子的情况,假设分别是 l,r ,那么就有:

    fu,i=pu(j<ifl,ifr,j+fr,ifl,j)+(1pu)(j>ifl,ifr,j+fr,ifl,j)

    大概是每个点会贡献给一段前缀和一段后缀的样子,考虑线段树合并。往下合并的时候,如果往左儿子区间走,就把右儿子区间的贡献算上,另一侧同理。支持区间乘,维护区间和即可。时间复杂度 O(nlogn)

  • Slay the Spire

    由于所有强化牌都是整数且大于 1 ,所以选了一张攻击牌之后尽量选强化牌一定不劣。所以一个自然的想法是枚举最大的攻击牌,然后再枚举抽了多少张强化牌,强化牌的乘积贡献显然可以 O(nm) dp 预处理,最后还剩下一些可以抽的和可以选的攻击牌,还需要枚举一下最小的一张被选的牌才能计算,这样做时间复杂度是 O(n2m) 的。

    考虑直接枚举选的攻击牌中最小的一张,再枚举抽了多少张强化牌,把所有选择的最小攻击牌是当前枚举的这张攻击牌的总贡献一起算。通过枚举的这两个量可以算出还可以选的攻击牌的数量和必须抽但不会选的攻击牌的数量,对于前者,可以预处理后缀和来算出比当前牌大的所有牌的贡献,乘上一个组合数即可;对于后者直接乘上它对答案的贡献即可,也是一个组合数。时间复杂度 O(nm)

  • 随机算法

    最大独立集可以状压 dp 计算,而对于题目中给的算法,可以转化成每次随机一个点尝试加入最大独立集,所以也可以状压 dp 计算当前集合算出来答案是对的的概率。时间复杂度 O(2nn)

  • 随机游走

    题目相当于是求到达 S 里面的点的最晚时间的期望,考虑用 min-max 容斥转化成到达 S 里面的点的最早时间的期望,即:

    E(max(S))=TS,T(1)|T|+1E(min(T))

    对于所有 T ,求出 E(min(T)) 后,可以高维前缀和处理出所有的 E(max(S)) ,便可以 O(Qn) 解决所有询问。

    对于每一个 T ,考虑树形 dp 求 E(min(T))fu 表示从 u 出发最早到 T 集合里的点的期望步数。若 uS,则 fu=0 ;否则下一步随机选一个邻居,即是 fu=fvdu+1 ,直接高斯消元的话对于每个 T ,时间复杂度是 O(n3) 的。考虑自底向上逐个消元,把所有 fu 表示成 kuffau+bu 的形式即可直接递推出 fx 。计算的时候要乘逆元,所以总时间复杂度是 O(2nnlogM+Qn)

  • 猎人杀

    发现可以把过程转化成每个猎人有 wi 个颜色为 i 的球,颜色相同的求没有区别,把这些球随机生成一个排列后依次取球,杀掉对应的猎人,如果已经死了就跳过。这样转化是对的,因为每次杀掉一个新的猎人的概率和原来的概率是一样的。那么现在就是要求每个颜色的球的第一次出现位置中,1 最晚出现的概率,还可以再转化成方案数。枚举 1 第一次出现的位置 i,那么 i 前面的是所有非 1 球至少出现一次的多重排列,i 后面是剩余球的多重排列,容易用 GF 表达,假设总共有 m 个球:

    (i1)!(mi)![zi1]j=2n(k=1wjzkk!(wjk)!)

    分治 NTT 计算即可,时间复杂度 O(nlog2n)

  • 斗地主

    可能的牌并不多,可以直接爆搜。

THUPC2018

  • 赛艇

    容易转化成地图的移动加障碍的覆盖。把移动也看成一个障碍矩阵 g ,原矩阵看成 f ,那么覆盖次数相当于是做一遍二维卷积,可以令 i=2mx+y 来把二维卷积压到一维。输出的时候注意边界对范围的限制,如果用 NTT 实现的话模数不能开 998244353 ,因为 2 的幂次不够用,可以选用 469762049 。时间复杂度 O(nmlog(nm)+k)

  • 好图计数

    容易发现连通的和不连通的好图数量相同(点数大于 1 时),那不妨数连通的好图。假设 F(z) 为连通的好图的数量,考虑它是由若干个小的连通的好图放在一起,组成一个不连通的图,然后取反得到的,所以可以得到:

    F(z)=E(F(z))F(z)1+z

    因为至少得有两个连通图,所以要减 F(z) ,后面的 1+z 是边界的调整。牛顿迭代即可做到时间复杂度 O(nlogn+T) ,但要写 MTT,常数过大,我目前没过。

  • 《算法与数据结构》小助教招募通知

    假如最终向量的方向确定了,那么贪心地,选择在该方向投影最大的几个向量即可。考虑当这个方向在旋转的时候两个向量的大小关系最多只会变两次,所以关键时刻只有 O(nm2) 个,对每一个时刻暴力做即可做到 O(nm2log(nm)+qn2m2) 。发现当两个向量排名发生变化的时候,只会影响这一维 c 等于某个定值的询问的答案,而由于数据随机,每次期望有 O(qm) 个这样的询问,动态维护一下即可做到 O(nm2log(nm)+qnm) 。由于存在向量重合,多个向量同时交换排名的情况,可以用扰动法处理,参数比较玄学。

  • 淘米神的树

    当初始时只有一个点是黑色的时候,假设其是 a ,那么有个熟知的结论是答案为:

    n!i=1n1szi

    szi 是以 a 为根时 i 子树的大小。考虑到有两个点 a,b 是黑色时,可以建一个虚点 c ,连边 (a,c)(b,c) ,初始时只有 c 是黑色,显然两个问题等价。于是树就变成了一棵基环树,套路地,考虑环上最后一个被染色的点,它左右两边有一条边就是没用的,所以枚举删哪条边,对于一种方案会算两次,所以最后除以二即可。对于非环上的点,它的 sz 是确定的,直接算到答案里即可。对于环上点,假设第 0 个点是 c ,顺次给其他点编号 1kk 是环上除了虚点以外的点数,那么容易推导出它们的贡献是:

    i=0kji1|sisj|=i=0k(1)kiji1sisj

    si 是环上第 i 个点 sz 的前缀和。 这个式子和拉格朗日插值的式子很像,考虑用多项式快速插值的套路加速。由洛必达法则:

    ji1sisj=limxsixsij=0mxsj=1F(si)

    其中 F(x)=i=0m(xsi) ,可以分治 NTT 计算,再多项式多点求值算出所有 F(si) 即可。时间复杂度 O(nlog2n)

  • 琪亚娜之墙

    容易发现多边形的包含关系是一棵树,可以扫描线处理出树形结构。把连通性相同的区域看成点,区域面积看成点权,多边形看成边,那么问题就转化成了边有两种颜色,需要支持翻转边颜色,查询点所在某个颜色的连通块点权和,用两棵 LCT 分别维护黑色树和白色树的大小即可。至于如何定位查询的坐标对应树上哪个点,可以把询问离线下来,在扫描线的时候一起处理。不过这道题的 std 是写得有问题,数据是错的。

  • 城市地铁规划

    显然最后是一棵树。便利度只跟度数有关,考虑直接 dp 出度数的方案。因为合法的度数序列里每个元素都大于 0 (除了 n=1),所以默认一开始每个位置填 1 之后,背包容量是 n2 ,所以不需要记录装了几个数,时间复杂度是 O(n2) 的。知道度数序列之后,可以给每一个度数是 1 的点连一条到度数大于 1 的点,最后再连一条边即可,容易归纳证明其必然有解。

posted @   Sword_K  阅读(97)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示