JSOI部分题解
JSOI2019
神经网络
考虑把依次经过的树写成一个序列并放在一个环上,那么这个序列所要满足的条件就是开头必须是(1),相邻的两个数不相同且首尾不能均为(1)(如果均为(1)的话会算重)。
对于每一棵树,我们可以设(f_{i,j,0/1/2})表示以(i)为根,将这棵树划分为(j)条链,(i)已经连了(0/1/2)条边的方案数,这样就可以把一棵树划分为(i)条链的方案数算出来了,因为这题链有方向,所以节点数(>1)的链方案要乘(2)。
令(f_i)表示某棵树划为(i)条链的划分数,构造EGF。
首先我们不考虑(1)的情况,那么有
其中(i!)是他们内部排列数,({i-1choose j})是强制相邻的两个并起来的方案数,((-1)^j)是容斥系数。
把(1)考虑进来,就是我们钦定(1)中的第一个数必须放在第一个位置除去环的限制,它不参与内部排列也不参与外部排列,那么有
最后再减去考虑首尾均为(1)的情况,那么就是最后一个点不参与外部排列但参与内部排列:
最后把所有EGF卷起来把([x^i])乘上(i!)再相加就是答案。
精准预测
这个题的约束条件很像( ext{2-sat}),往这方面考虑。
题目所给的约束条件可以直接连边,还有约束条件的话就是如果一个人在(t)时刻活着那么在(t-1)时刻活着,在(t)时刻死了那么在(t+1)时刻死了。
然后再看每个点(T+1)时刻死了能到达多少个其他点活着的就行了,假设有(cnt)个,那么该点的答案就是(n-cnt-1),这个可以通过( ext{bitset})优化拓扑排序dp求出。
但是( ext{bitset})还是存不下所以要分段跑,注意如果一个点必死那么它不能算进贡献。
节日庆典
可以维护出可能出现的最小的后缀的集合,发现集合中的数不超过(log n)个。
证明参考 zsy 的:
简单证一下这个性质:考虑相邻的两个最小后缀(i,j),若(|j|<|i|<2|j|),则说明(i=AAB,j=AB),其中(A,B)均为字符串,且(B)是(A)的一个严格前缀。此时考虑(i=AAB,j=AB,k=B),可以发现无论如何最小后缀都会在(i)和(k)之间产生,(j)不可能成为最小后缀(如果(j)比(i)优,那么一定有(k)比(j)优),由此说明相邻两个最小后缀的长度至少( imes 2),因此数量不超过(log n)。
这题的数据范围较大,所以维护(lcp)时食用( ext{Z-algorithm})维护。
JSOI2018
战争
问题转化为给定你两个凸包(mathbb S,mathbb T),每次独立的询问将(mathbb T)中的每个点移动一个向量,问(mathbb S,mathbb T')是否有交。
我们知道对于两个凸包(mathbb {A,B})的闵可夫斯基和为(mathbb C={vec ainmathbb A,vec bin mathbb B;|;vec a+vec b}),
而两个凸包对于一次移动向量(vec w)有交,则存在(vec a=vec b+vec wLeftrightarrowvec w=vec a-vec b)。
那么构造(mathbb Q={-vec t;|;vec tin mathbb T}),构造(mathbb P=mathbb{S+Q}),每次判断是否有(vec winmathbb P)即可。
列队
可以贪心地想,将([l,r])内所有人的休息点从小到大排序后依次放入(K...K+r-l)答案是最优的。
那么现在问题主要转化为如何快速求(sum_{iin [l,r]} |a_i-(K+i-l)|)(假设(a)已排好序)。
这时候可以对于原序列({a})建主席树维护个数及(a)的和,考虑拆掉绝对值的话就是二分(mid),表示(a)大小前的(mid)名均有(a_ileq K+i-1),因为(a)两两不同所以可以保证存在一个这样的分界点。
直接在外面二分后用等差数列求和算答案复杂度为(O(nlog^2 n))可以获得(70pts),改为在主席树上二分复杂度(O(nlog n))可获得满分。
潜入行动
有一个显然的dp是设(f[x][i][0/1][0/1])表示(x)的子树内安装了(i)个监听器,他自己不选/选,他父亲不选/选的方案数。
转移较为繁琐而且容易实现,这里不再赘述。
考虑复杂度:乍一看复杂度为(O(nk^2))但是仔细思考发现满足度数为(k)的点最多(frac nk)个,那么最终复杂度为(O(nk))。
防御网络
题意:求从一个仙人球中任选一个点集所构成的斯坦纳树大小的期望。
考虑每条边的贡献,如果一条边不再环内的话贡献很好算。
那么主要问题就是解决环上的边怎么算贡献,只考虑某一个环,如果一个点的子树内选择了点的话就把环上这个点给标记出来,那么最后选择的东西就一定是整个环的长度减去相邻两个被选中的点的最大距离。
设(f_x)表示相邻两点最大长度不超过(x)的方案数,那么对于一个(x),它的方案数就是(f_x-f_{x-1})。
因为是一个环,断环为链后枚举最左被选择边的点就可以消除原来环的影响。
剩下的部分考虑dp,设(g_i)表示当前在第(i)个点且强制选第(i)个点的方案数,转移时强制长度差不超过(x)前缀和优化即可。
最终复杂度(O(n^3))。
绝地反击
这里提供两种方法:
方法一:
是我自己想到的,对于圆上点的排列,假设所有点均由正方向旋转度数(alpha),那么最优解函数(f(alpha))一定为一个单峰函数。
三分(alpha)后再二分时间(t),将到达时间(leq mid)的我方舰队向敌方母舰连边,跑二分图匹配看是否有完美匹配即可。
方法二:
二分时间(t),那么对于每艘战舰在圆弧上的交点至多只有(2)个,而对于旋转,可以发现本质不同的转法一定是有一个点转到了这(2n)个交点中的其中一个。
我们将这(2n)个转角极角排序(这(2n)个点可用余弦定理算出),则每次改变转角最多加入或删除一组匹配,手动模拟退流即可,复杂度(O(二分图匹配 imeslog n))。
JSOI2016
最佳团体
考虑01分数规划,那么我们现在二分了比值(mid),每个点有一个新的权值(c_i=p_i-s_i imes mid),
现在要求在满足条件的情况下使(sum c)最大,树形背包即可。
独特的树叶
因为是树的同构问题,考虑树哈希。
有一种异或的树哈希方法((f)为哈希数组):
通过换根可以求出以每个点为根的(f)值,将(A,B)树分别哈希并将(A)中的每个哈希值丢到set中,如果有(B)中有一个叶节点的父亲去掉(xor)对应叶节点的(f)值后这个哈希值不在这个set中,说明对应的叶节点就是那个多余的点。
扭动的回文串
单独存在于(A,B)串中间的回文串可以直接( ext {manacher})求出,考虑“扭动”形成的回文串。
枚举回文中心,因为“扭动”的回文串,必然在(A)或(B)中对应有一个最长的回文串,这是( ext{manacher})就已经求出来了的,再用二分+哈希把两串拼一起所形成的新串能够再拓展的最大长度弄出来即可。
灯塔
其实就是要你对于每一个(i)求出(p_i=lceilmaxlimits_{j}(a_j-a_i+sqrt{|i-j|}) ceil)。
有一种做法就是对于每个(i),(sqrt{|i-j|})的取值不超过(sqrt n)个,而且对于每个(i)和某个取值(x),所对应的(j)一定在一段或两端连续的区间中,可以用st表查询最大的(a_j),复杂度(O(nsqrt n))。
另外一种做法只考虑(j<i)的情况,之后通过( ext{reverse})求出(j>i)的情况,而可以发现决策点具有决策单调性,用决策单调性优化可以做到(O(nlog n))。
位运算
假设我们选出来的数(A_1,A_2...A_N)满足,(A_0> A_1geq A_2...geq A_N),其中为了方便我们令(A_0=R)。
考虑状压这(N+1)个数的大小关系,那么状态(s)有(N)位,每一位(0/1)表示(A_i(> ext {or}=) A_{i+1}),设(f_{i,s})表示当前从高往低考虑到第(i)位大小关系为(s)的方案数,枚举当前位的填数情况(t)可以进行转移,而对于每个循环的转移时一样的,处理完一个循环的情况后可以矩阵快速幂求出方案数。
炸弹攻击
这题比较玄学,网上所查得到的大部分题解说是模拟退火实则爬山算法。
就直接按照随机化的方法( ext{rand})一个移动向量看是否更优即可,温度、温度变化量、eps以及一些退火中的tricks网上都有,这里不再赘述。
飞机调度
首先可以想到( ext{floyd})求出任意两点之间的最短路,因为存在维修时间所以要将(dis_{i,j})设为(g_{i,j}+p_j)。
假设某一趟航班(i)从(x_i
ightarrow y_i),时间由(s_i
ightarrow t_i(t_i=s_i+T_{x_i,y_i}+p_{y_i})),那么对于航班(j),如果想让(i,j)共用一趟飞机则必须满足(t_i+dis_{y_i,x_j}leq s_j)。
我们将(i
ightarrow j)连一条边,发现整张图一定是个DAG,直接跑最小路径覆盖即可。
无界单词
首先考虑第一问,设(f_i)表示长度为(i)的(|border|=0)的串的个数。
我们考虑容斥,减去(|border|>0)的情况,因为如果(|border|geq lceilfrac i2
ceil),那么可以减去中间公共部分变成(<lceilfrac i2
ceil)的情况,所以有
对于第二问考虑二分,则问题实际上变为求前缀为(s)时的(f_i)。
显然有(f_i=[前i个字符无border],ileq |s|)。
对于(i>|s|),则分类讨论之:
首先有
其中(c_{i,j})表示系数,有:
然后按照这种方法转移就好了。
轻重路径
首先可以用树状数组单点加、区间查询维护每个点的(size)。
考虑这一次我们删掉了叶子(x),那么对于(x)到根(rt)的路径,如果要满足上面的一个点(u)它要改变他由重变轻的话必有(size_uleq frac {size_{rt}}2),我们可以二分这个点,那么这样的话最多二分(O(log n))次。
判定一条边是否从重边变成轻边的依据是父亲的重儿子之前指向(u),同时删除节点后有 (size_u+1=size_{another\_son}),注意特判(u)是父亲子树最后一个节点的情况。
复杂度(O(nlog ^3n))但是树状数组以及树剖、二分的常数都比较小,还有这题的思路感觉比较类似于CSP2019树的重心。
病毒感染
题目的意思是如果往左走就要把之前左边没治愈的治愈完。
那么整个过程可以分为若干个子段,每个子段治完子段中的所有村庄然后再回到该子段的起点继续治接下来的子段。
设(f_i)表示目前在(i)且(1...i)均治愈完毕的最小代价,转移的话枚举这个子段的起点:
其中(s)为前缀和(g_{i,j})为(i)走到(j)再回到(i)所花费的最小代价。
考虑(g_{i,j})由(g_{i+1,j})转移过来,枚举(i)在不在当时治愈有:
然后就可以dp了。
反质数序列
考虑到奇数和奇数、偶数和偶数之间不可能为质数,那么所有元素就构成了一个二分图。
其中奇、偶之间如果是质数的话就连一条边,答案就是最大独立集也就是(|mathbb V|-)最大匹配,注意(1)选的个数不能超过(1),所以(1)只要保留一个。
炸弹攻击2
考虑枚举每一个发射源,算出每个发射源的贡献。
将这个发射源作为原点,将其他除了发射源之外的点放一起极角排序,因为题目保证所有敌人都在己方单位上面,所以答案就是两个极角不超过(pi)的激光塔之间所夹的敌人数,又因为这题实际上是个环的结构,所以需要断环为链+前缀和维护。
JSOI2015
salesman
直接树dp+贪心即可。注意如果选了某个子树的权值为0那么也说明不唯一(可以不选)
isomorphism
很容易就可以(O(n))把简化之后的树给建出来,建出来之后用树哈希判一下即可。
这个题不用像[JSOI2016]独特的树叶
那样把每个点为根的哈希值求出来你就直接求一下重心的哈希值就好了,注意重心可能有两个。
子集选取
每个数都是独立的,答案一定是(w^n)的形式。观察发现答案就是(2^{nk}),可以列出递推式然后归纳证明。
送礼物
首先考虑二分答案。
那么就是要判断是否有
化简得:
发现最优解一定是左右端点分别为最大/最小值,不妨令右端点取到最大值,左端点取到最小值(可以( ext{reverse})得到另一种情况),那么我们将数组(a)中的每个数(a_i)新赋权值为(a_i-i imes ext{mid}),跑一遍单调队列即可。
注意有可能区间长度(leq L)而不存在上述情况,需要将答案对于(max_{i=1}^{i+L-1}frac { ext M(i,i+L-1) - ext m(i,i+L-1)}{L-1+K})取max。
字符串树
从根节点往下建立可持久化( ext{Trie}),对于每个询问直接查即可,操作都比较模板,这里不再赘述。
非诚勿扰
根据期望的线性性,我们的答案就是(sum_{i=1}^Nsum_{j=i+1}^N sum(i,j)),其中(sum(i,j))表示(i,j)之间的贡献。
那么这个(sum(i,j))就是直接对于(j)的每个如意郎君,算出(i)每个编号比他大的被选中概率之和(s),然后用这个如意郎君被选中的概率( imes s)就可以算出。
而这个东西是可以按照女生编号从小到大然后用树状数组维护的,我们需要解决的最后一个问题就变为了如何求出在长度为(l)的列表中,第(k)个男生被选中的概率(P)。
那么有:
然后就做完了。
套娃
对套娃按(B_i)排序,按顺序处理每一个套娃的内径,在所有套娃的外径中找到一个可以套入当前内径的前提下最大的外径(也可能找不到),将其套入该内径,并删除之。
正确性证明:令当前套娃好看度为(B_i),若被上述算法选中的外径没有被套入这个套娃中,而是套到了一个(B_jleq B_i)的套娃中,那么交换(i)和(j)内部的外径不会使方案变得更劣。
(该题题解来源于cz_xuyixuan)
最小表示
如果对于一条边(u ightarrow v)存在另外一条(u ightarrow v)的路径,那么这条边就可以删去,而且每条边之间互不影响。
那么用( ext{bitset})对每个点分别维护可以到达它的集合,那么对于一条边(u ightarrow v),如果有(u)的出点集合(mathbb S)及可以到达(v)的点集(mathbb T),使得(|mathbb Sigcap mathbb T|>1),则满足上述情况,可以删去。
圈地
经典的二元关系最小割模型,如果一块地在残余网络中与(S)和(T)联通分别表示这块地被两人之一,而两块地之间的连边表示修墙的代价,割一条边就可以表示出修一堵墙。
串分割
因为肯定要使位数最多的数位数最少,所以可以算出来位数最多的数有多少个以及多少位(记为(len))。
因为是个环所以考虑断环为链后倍长,再后缀排序一遍就可以二分答案了。
枚举每个开头位置(i),从(i)往后跳,如果(rank_i> ext{mid})那么这个数就不能有(len)位,只能往后跳(len-1)位,否则就跳(len)位。
最后判断一下最终位置是否(geq i+n)即可。
染色问题
这种限制条件一堆的题考虑容斥。
枚举,至少不涂(i)行、至少不涂(j)列,至少不用(k)种颜色,那么式子就很显然了:
最大公约数
首先对于所有以一个位置(i)结尾的子段(gcd)不超过(log)个且连续。
那么直接用( ext{map})维护一下每种(gcd)的最前面的出现位置,每加入一个数暴力更新一下即可,复杂度(O(nlog^2))。
地铁线路
这道题提供两种方法:
第一种方法是我自己的方法:
首先考虑第一问,对于一条线路(a_1,a_2...a_L),新建两排点(b_1...b_L),(c_1...c_L),同时连边(b_1
ightarrow b_2...
ightarrow b_L),(c_1leftarrow c_2...leftarrow c_L),边权均为(0),表示一条线路的来回。再连对于(forall i),连(b_i
ightarrow a_i,c_i
ightarrow a_i),边权均为(0)表示下车,(b_ileftarrow a_i,c_ileftarrow a_i)边权均为(1)表示上车,这样子由(S)跑一遍(01bfs)可以解决第一问。
再考虑第二问,其实已经很好解决了,直接在最短路图上面跑一遍拓扑排序就可以了,需要注意如果是上下车贡献是不会增加的。
第二种方法是网上的方法:
第一问对于每条线路新建一个点,线路上的站向线路连边权为(1)的边,线路向线路上的站连边权为(0)的边,然后(01bfs)。
第二问考虑dp。设(f_i)表示到达(i)时最多乘坐多少分钟的地铁。
假设一条线路的(dis)为(d),则我们可能在(dis)为(d-1)的站上车,在(dis)为(d)的站下车,所以我们要用 (dis)为(d-1)的站更新(dis)为(d)的站。
把所有线路按(dis)从小到大排序,然后对于每条线路转移:(dp_i leftarrow dp_j + dis(i, j),dis_i = dis_j + 1)。记一下前后缀最大值即可(O(n))转移。
JSOI2014
拼图
看到(N imes (M=sum w)leq L=10^5)考虑分块。
接下来分类讨论:
-
(N<M)
考虑枚举答案在哪两行之间,如果在一个块内我们可以直接求出来。
如果在多个块中间的话,我们肯定把中间全白的块放在中间,然后再在左右分别放上右边/左边白色列数最多的块。注意可能会重复那么我们选次大的(还有些实现细节详见代码)。
此时复杂度为(O(N^2M)=O(Lsqrt L))。 -
(N>M)
预处理出每个点最多向上延伸到哪一行(up),我们枚举时就直接以某个点所在的行为下界,以那个点的(up)为上界可以用(O(NM))代替原来的(O(N^2))枚举。
我们再用原来的方法可以(O(M))求出在当前状态下的最大答案。
此时复杂度为(O(NM^2)=O(Lsqrt L))。
宅男计划
现在假装我们知道了购买外卖的总次数(k),那么我们的问题就变成了如何确定每种外卖购买方案。
首先将所有外卖按照价格排序,显然只要没有过期那么一定会购买尽可能多的更便宜的外卖。
假设对于最便宜的外卖,其过期的天数是(s),那么我们一定会在每次购买的时候都买(s)份,那么接下来的每一份外卖都是类似的处理。
这个总次数(k)是可以三分的,但是考场上如果不放心可以选择模拟退火。
骑士游戏
设(f_i)表示打死怪物(i)的最小代价,转移很简单,但是这个转移是有环的,我们换个方式,用(spfa)转移即可。
支线剧情
上下界网络流板子题,每条边下界为(1)再跑费用流即可。
奇怪的计算器
将所有询问离线并从小到大排序。
那么在([L,R])之间的数一定是一段区间([l,r]),将([1,l-1])区间赋值为(L),([r+1,Q])区间赋值为(R)即可。
头铁的话可以直接维护所有操作,但是这里有个小 trick :
考虑这样一个函数(f(a_1,a_2,a_3)=a_1 imes c_i+a_2 imes a_i+a_3)
其中(c_i)代表(i)这个位置现在的数, (a_i)代表(i)这个位置原来的数,
那么每次操作都可以看做对应位置加这个函数:
- 加(k ightarrow f(1,0,k))
- 减(k ightarrow f(1,0,−k))
- 乘(k ightarrow f(k,0,0))
- @(k ightarrow f(1,k,0))
线段树维护一下每个位置的(f)就好了。
支线剧情2
设(f_i)表示只在(i)存档访问完以(i)为根的子树的最短时间,有
其中(s_i)表示(i)子树内叶子结点的个数。
再设(g_i)表示可以在(i)子树内存档的最短时间,考虑转移:
对于一个子节点(j),
要么不在(j)的子树内存档,此时贡献为(f_j+s_j imes cost_{i,j})
要么把存档点移至(j),此时贡献为(g_j+cost_{i,j}+dis),(dis)表示(i)到根的距离。
还需注意一下有一个子树的贡献可以变为(g_j+cost_{i,j})。
答案即为(g_1)。
强连通图
第一问可以直接缩点,考虑怎么做第二问。
缩点后将所有入度为(0)的强联通分量(共(n)个)和所有出度为(0)的强联通分量(共(m)个)拿出来分别排在左边和右边,如果可以到达就左边向右边连有向边,那么原图构成一个二分图且满足每个点都有入度或出度。
我们的目标就是使这个二分图强联通。
假定答案下界为(max{n,m}),那么答案一定不小于因为如果这个二分图存在完美匹配肯定最优(可以直接连交错环),而此时都需要(n+m-min{n,m}=max{n,m})步。
接下来考虑构造这个下界。
从二分图中找一个极大匹配(mathbb S),那么在这个匹配中,连接交错环可以做到(frac{|mathbb S|}2)步。
因为这个匹配是极大的而且每一个点都存在入度或出度,所以不在该匹配(mathbb S)中的点一定可以从(mathbb S)到达或被到达。
分别我们选择左右两个点连接交错环,那么此时(Delta)步数=(Delta)点数/2。
对于剩下的没有匹配的点,均可一步连到这个匹配上,此时(Delta)步数=(Delta)点数。
综上,我们共用(min{n,m}+(max{n,m}-min{n,m})=max{n,m})步,构造完毕。
歌剧表演
可以发现进行一次演出就可以将当次演出的演员与其他演员区分开来。
根据这条性质,我们一开始将所有演员存在一个( ext{set})中,第一次演出是将演出成员从( ext{set})中删除再加入一个新的( ext{set}),第二次演出时再将两个( ext{set})中的演员从所在( ext{set})中删除再分别放入新的两个( ext{set})中,以此类推。
发现这样子做的话所在( ext{set})只有他一个的演员可以分辨出来。
士兵部署
转化下题意:给定一个凸包,每次新加入一个点,问每次加入这个点后的新的凸包面积。
首先在凸包内凸包面积肯定不变,否则要求出这个点关于凸包的两个切点。
选一个凸包内的点作为新的原点,将所有询问离线后按极角排序,每个切点可以在与当前点极角差不超过(pi)的凸包上的点中二分后用叉积判断,注意你已经将询问离线所以二分的范围你是可以依次尺取出来的。
其中还有断环为链的细节等详见代码。
回文串
对于一段区间([l,r]),令(mid=(l+r)/2)我们可以将其转化为([l,mid])和([mid+1,r])的贡献之和,这样子做的好处是可以确定一个超出([l,r])的回文串到底是先触碰到哪个边界。
这样子的话我们可以对于未超出边界的与超出边界的分开算贡献,用主席树维护即可。
电信网络
最大权闭合子图模板题,不再赘述。
打兔子
首先注意到如果打过了的地方和打过了的相邻的地方你都不会再打,那么我们就可以分类讨论一下(1,n)是怎么打的把环的限制去掉,然后设(f[i][j][0/1])表示目前打到第(i)块地,开了(j)枪,这块地打没打的最优值即可,转移比较简单。
注意一下这个状态如果(Kgeq lceilfrac N2 ceil)时不满足,但是你可以直接打完所有兔子,还要注意下(N=3;K=2)的情况。
序列维护
线段树模板题
学生选课
考虑二分答案,之后剩下的二元关系直接用( ext{2-sat})进行( ext{check})。
矩形并
首先有一个比较巧妙的算答案的方法:将所有矩形内部进行矩形加一的操作,然后依次查询所有矩形内部的权值和,加起来,最后再减去所有矩形的面积和,所得的结果再除(frac{n(n-1)}2)即为答案。
考虑怎么进行矩形加,矩形查询操作。
我们现在先看一下普通的二维树状数组怎么完成上述操作:
其实就是要求
记差分数组(d),使得
那么就有
可以拆成四个树状数组分别维护。
至于这题,我们可以将一维离线下来然后另外一维树状数组维护完成。