2024.6 #征集标题
QOJ857
给定一棵 nn 个点的树,再给定一个点集 S={p1,p2,⋯,pk},保证 S 在树上构成一个独立集。
形式化地,不存在 i≠j 满足 pi=pj,不存在 i≠j 满足 pi,pj 在树上相邻。
给定另一个独立集 T,现在你需要进行若干次操作,每次操作你可以选择一个 u∈S,选择一个和 u 相邻的点 v,然后将 u 从 S 中删除,并将 v 插入集合 S。你需要保证操作之后 S 仍然是独立集,且所有操作完成后有 S=T。试构造一种方案在 4n2 次操作内完成该目标,或报告无解。
可以证明,若存在一种方案,则一定存在操作数在 4n2 次操作内的方案。
数据范围:有 T 组数据,1≤T≤105,1≤n≤2000,∑n2≤4×107。
对于一个独立集 S,我们考虑给他对应一个独立集 PS 满足 S 可以通过操作得到 PS,且每个 S 都可以唯一对应一个 P。这样对于初始集合与结束集合 S,T,如果 PS=PT,我们就可以构造一组解;否则一定无解。
设点 u 的深度为 depu,将所有点按照 dep 降序排序(dep 相同的可以任意排序),下面我们构造一个 PS 满足 PS 内的点按照刚才我们定义的顺序排序后,字典序尽可能小。
我们按 dep 降序依次考虑每个点,每次遇到一个点的时候,我们在保持前面的点不动的情况下尽可能地插入这个点。设当前点为 u,如果 u∉S,我们枚举 v∈S,尝试把 v 移动到 u。
不妨假设 v 到 u 路径以及和这条路径相邻的点中没有别的点 u′∈S(否则我们总可以移动 u′→u)。如果 v 可以直接移动到 u,那么我们直接把 v 挪过来就好;否则,由于 u→v 路径上没有别的在 S 内的点,唯一的情况是 u→v 路径上的倒数第二个点 x,他的相邻节点中存在别的点 v′∈S。
那么此时我们需要尝试把 v′ 挪走。以 v′ 为根做 DFS,对于点 y,我们在 DFS(y) 结束的时候尝试把 y 往某个儿子的方向挪动一格,如果这个都做不到那么 y 一定动不了;否则我们就成功把 y 挪下去了。在 DFS 结束后我们检查 v′ 是否被挪下去,那么如果能成功把 x 的儿子中别的点都挪下去我们就可以把 v 移动到 u,否则 u 一定不可能被加入进 S。
由于我们是按 dep 降序考虑每个点,在任意时刻,任意两个未被固定的点 x,y 的路径上都不会需要经过某个被固定的点 z,于是这样做不会影响到前面被固定的点。
综上,我们可以在至多 2n 步内加入一个点 u,于是做变换 S→PS 的步数不超过 2n2,总步数不超过 4n2。
Phoenix and Diamonds / CF1515I
首先我们考虑 ai=∞ 怎么做,这种情况下相当于每次 ans←ans+⌊c/wi⌋×vi,c←cmodwi。
注意到每次只要 c≥wi,那么取模之后至少减半,于是我们二分出下一次修改的位置即可。
现在考虑有 ai 的限制怎么做,注意到如果 wiai<c 那么此时 i 一定全选,如果 wi>c 那么 i 一定全不选。换言之这种情况下,选的个数在 [1,ai) 中的物品个数仍然是 O(logc) 级别。
考虑怎么快速找到下一个没有被全选,且至少选中了一个的物品,额发现我不会找,6
把这个过程看作有一个长为 ∑ai 的序列 w,每次如果 c≥wi,就 c←c−wi,ans←ans+vi。
考虑倍增分块,如果当前 c∈[2k,2k+1),我们分别找到所有 wi∈[1,2k) 与 wi∈[2k,2k+1),那么后者只要选中一个就会导致 c 直接坠机到 c<2k;对于前者,只要 c≥2k 耐摔王就一定是一直选。
称前者为小物品,后者为大物品。
设当前在位置 p,我们找到第一个 q 满足 wq∈[2k,2k+1),且 c−∑i∈[p,q),wi<2kwi≥wq,也就是说 q 是第一个选的大物品。如果不存在这样的 q,则说明 c 是一路选择小物品坠机到 c<2k 的。
不论哪种情况,我们都可以线段树二分得到坠机的具体位置。由于一共只会坠机 O(logw) 次(这里我们只需要找到第一个没办法全部选完的 (ai,wi),那么在选完这个物品之后一定有 C<wi,于是就把 C 缩减到了 w 的范围,就只需要 logw 而非 logC 次),因此总复杂度为 O(nlogw+qlognlogw)。
QOJ7973
首先把前缀和调对,维护一个堆,如果某个时刻 si<0,我们就找到堆里面最小的满足 j 是左括号的 aj 把他改过来;接下来把总和调对,这时需要把 ( -> )
,这个相当于 i 后面不能超过 si/2 个 ( -> )
。从小到大依次考虑每个 ai,如果能填就填,一定是最优的。
我们发现两个过程几乎是独立的:找到全局 s 的最小值 p,那么第一个过程一定全部发生在 p 之前,第二个过程一定全部发生在 p 之后(因为只要能把 sp 修改到 sp≥0 显然后面的也 ≥0 了;最后的 s 中一定有 sp=0,所以第二个过程的修改只能在 >p 的位置进行)。
考虑修改一个 ai,有什么影响。首先我们考虑第一个过程,发现 si 是不会变的,只是修改了哪些 ai 可能会改变。我们按照 i 去找 j 可能不太好找,考虑提前预处理出来这样走一遍会有 si<0 的 i,称这些点为匹配点,那么相当于每个匹配点 i 会往前匹配一个 j,求权值和最小的完美匹配。
发现此时问题就变成了人员调度,不过他是在链上做,所以 1log 也很容易,讨论一下最优匹配集合中至多一个点的变化即可。总复杂度 O((n+q)logn)。
我没怎么写过这类链上模拟费用流类,这里捋一下:相当于长度为 n 的序列内有若干左部点和右部点,每个左部点可以匹配它后面的一个右部点。根据 Hall 定理,只需要每个左部点集合它们能用的右部点的并集够用。那么一定是取一个后缀的左部点,维护 wi 表示 [i,n] 内的左部点个数减右部点个数,则只需 wi≥0。
加入一个左部点 i 的影响是把所有 j≤i 的 wj←wj−1,用一个线段树维护 w 即可。考虑修改一个 ai,如果 ai 本来就没有被选中,那么增大 ai 他仍然不会被选中;否则 ai 可能会被选中。考虑如果选中 ai 会导致 j≤i 的 wj←wj−1,此时一定存在一个 wj=−1,找到最靠右的一个,那么我们肯定得从 [j,n] 区间内选一个 ak 撤销。找到这个区间的最小值,判断 ak>aj 是否成立即可。
类似地,如果 ai 本来被选中了,那么减小他肯定仍然被选中;否则我们如果撤销他,那么会导致 j≤i 的 wj←wj+1。如果再选中 k,那么需要 [i,k] 区间内原先没有 j 使得 wj=0。
于是我们维护 w 的线段树只需要支持查询区间第一个 0,区间最后一个 0,区间 min,区间加;再对 ai 维护序列维线段树支持单点修改,区间 min 即可。对于后缀的情况,直接按照 a 从小到大考虑,相当于每个位置有个 wi 表示这前面变的括号数量不能超过 wi 个。然后和前缀就一样了。
QOJ7872
显然区间之间互相独立,考虑怎么算一个区间经过一系列操作 x1,x2,⋯,xk 之后变成什么样。加入一个 xi 的时候,区间 [l,r] 此时被划分成若干区间,加入他的影响相当于把至多一个区间再分裂一遍。
按时间轴倒序扫描线,维护一个数据结构支持插入一个 xi,查询 [l,r] 在经过这些操作后的期望长度。
日了啊,我不会做。。。。写了个 O(nqlogn) 给他爆了
考虑区间都在崩坏之前插入怎么做,把崩坏的点画出来,建笛卡尔树,那么查询一个区间相当于在树上一路递归下去。在笛卡尔树上做线段树合并,每个点 u 维护 fu,i,gu,i 表示这个点向右长度为 i 的区间的答案和向左长度为 i 的区间的答案,那么这个可以线段树合并+分裂维护;查询就找到第一次分裂的位置然后把这个点的 f,g 加起来。
那么回到原题就只需要加一个操作分块,设一块内有 B 个操作,每个区间经过 B 次分裂后会分裂成 O(B2) 个区间,总复杂度为 O(nB(B2logn+nlogn)),取 B=√n 即可。
实际上,在经过 m 次操作之后分出的区间个数在去重之后仍然是 O(m) 级别:只需要考虑两端点都是某个划分点的区间,这样的区间要么不交要么包含,于是形成树形结构,只有 O(m) 个。那么我们分治,每次递归进左边之后把得到的区间记下来,然后带着这些区间递归进右边。总复杂度 O(Qlog2Q)。
QOJ1875
先枚举答案的位数,然后把每一位按照 modk 分段,那么一个数是 10k−1 的倍数当且仅当每一段的数求和为 0。我们确定位数之后逐位填入,现在相当于有一个前缀已经确定填啥了,要算后面有多少种方案使得所有段的和是某个 x(10k−1)。枚举 x,从低往高填,记录之前的进位即可数位 DP。
复杂度没咋算,可能是三四个 log。
QOJ4892
相当于 ai,aj,ak 的中位数为 x,这又等价于 ai,aj,ak 中至多一个数 <x,至多一个数 >x。
2sat,建出 nV 个点 (i,j) 表示 [ai≤j],然后连边。注意有用的点只有 O(m) 个,只保留这些点即可。
时间复杂度 O(n+m)。
CF1310D
把所有点随机染色然后 DP,要求每次只走黑点和白点之间的边,即可。
算对的期望次数为 2K−1,总复杂度 O(N22K)。
LOJ6897
参考:https://www.cnblogs.com/myee/p/3-smooth-number.html
把每个数写成 2x3yz 的形式,其中 2∤z,3∤z,然后把所有数按照 z 分类。
对于 z 相同的一类,设 p=⌊log2⌊n/z⌋⌋,q=⌊log3⌊n/z⌋⌋,则我们只需要 p+2 个点即可覆盖这个等价类。一开始我的想法是,我们构造一个菊花,中心点的权值是 2p3qz,下面挂 p+1 个点,这些点就是那个杨表里面右下那个轮廓线上面的点。
这样会导致点权超过 11000,我们考虑其实你可以直接把那个轮廓线上的点连成一条链,然后每个内部点都可以从一个区间来得到。不过这样的话链上的某些点可能得不到,我们把整个杨表右移一格即可。
打表发现直接做得到的点数是 n 或 n+1。实际上,考虑到这个数是
发现 ∑2∤z,3∤z⌊log2⌊n/z⌋⌋+1 计数了所有不是 3 的倍数的数,总和为 n−⌊n/3⌋。还剩下一个 ∑2∤z,3∤z1,对 nmod6 分类,这样的 z 每 6 个中会有 2 个(即 1,5),但 nmod6=1,5 时会比 ⌊n/3⌋ 多一个,所以上式在 n≡2,3,4,6(mod6) 时为 n,其余时刻为 n+1。
考虑对于 2x>n 的数,我们的构造方法都是直接两个 x 连在一起。这样太蠢了,考虑如果 lcm(x,y)≤11000,我们就搞一个 3 个点的链 x,lcm(x,y),y,这样可以减少很多点数。可以发现,除了 n=5,都可以用这个方法通过,特判 n=5 即可。样例贴心地给出了 n=5 的 case。
可以发现我们甚至构造出来的是一条链,因此本题可以叫「约链」
Luogu P7597
考虑只要知道每个点的重儿子就可以简单得到 O(nlogn) 做法。
怎么确定一个点的重儿子呢,考虑按深度加点,每次我们在他的子树内随机一个点,然后以这个点所在的子树为重儿子。这样得到的剖分,其每个点到根路径上的轻边个数仍然是期望 O(logn) 级别。
CF1948G
怎么这个最大匹配是不带权的......那么最大匹配等于最小点覆盖,钦定一组点覆盖之后直接跑最小生成树即可。
复杂度 O(2nn2)。
QOJ1813
搜一下 N=4 的表,发现就算 N=4 也必须用完两次比较操作。
考虑把这两次操作留到最后,维护一个当前状态:我们知道当前 p1,p2,⋯,pi 这些数中的最小、次小、最大、次大的下标集合(但无法对应具体是哪那个数),且我们可以区分最小次小和最大次大的集合。具体来说对于这四个数,我们可以知道 x1,y1 表示最小、次小的下标集合(但不知道哪个是最小,哪个是次小),以及 v1 表示次小的数值。同理也知道 x2,y2,v2。考虑如何在 2 次操作内扩展。
我们做以下两个询问:QueryMid(x1,y1,i+1),QueryMid(x2,y2,i+1),分情况讨论:
- v1<pi+1<v2:这种情况具有的特征是两次询问的答案相同,且这个答案就是 pi+1,直接完成扩展。
- 否则,不妨设 pi+1>v2,那么这两次询问必有一次的答案是 v2,我们就可以唯一确定哪个数对应 v2;另一次的答案就是新的次大值,于是也可以完成扩展。
最后两次操作 1 确定具体排列即可。综上,我们在 2N−4 次 2 操作和 2 次 1 操作内得到排列。
APC001H
考虑一条链怎么做,我们从后往前做插入排序,具体来说,维护下标 i 满足当前 pi,pi+1,⋯,pn 是单调递增的。那么每次我们考虑 p1 在 pi,pi+1,⋯,pn 中的哪个位置,然后把它插入进去即可。
这样只需要 n−1 次操作即可排序。考虑树怎么做,每次我们找到所有叶子,对每个叶子 u 我们往上找到最远的点 v 满足 fav 有至少两个儿子,或者 v=1。如下图,标红的部分就是我们这次要还原的若干条链:
下面我们在 O(n) 次操作内把所有的这些链 vi→ui 排序。类似地,我们每条链都维护一个位置满足这个位置往下的那一段已经单调递增,而且段内的数都是的确应该最后放在这条链里面的。考虑当前根节点上的数,如果他在某条链内部,我们直接把它换到这条链的对应位置里;否则我们找到最深的一个,需要放到某条链里面的点 u,把 0→u 这条链上的点操作一遍,直到某个需要跑到某条链内部的点当前位于根节点。
完成这一阶段之后,我们删掉刚才找到的这些链,然后接着做下去。
这样每轮操作数都是 O(n),可以证明轮数是 O(logn),于是操作数为 O(nlogn)。
Code Festival 2016 Final I
感觉不如......[Code Festival 2017 Final I] Tournament
考虑一个格子可以换到哪里,发现顶多被换到四个位置,某些是两个位置。考虑这四个位置的形态,发现是可以任意排列的。不过不同的组之间不一定独立。
考虑一组内的元素怎样能在不影响其他组的前提下任意排列,发现可以得到所有偶排列(每个位置都被交换偶数次)。那么如果有两个相同的元素,他就可以任意排列;否则不行。
建出一张二分图,发现每个连通块独立;对于一个连通块,任取一棵生成树,发现生成树上的边就可以唯一确定所有的非树边。于是答案就是 2#Nodes−#Connected components。建图求连通块个数即可,时间复杂度 O(HW)。
Global round 26
E
原问题相当于要选取一棵原树的点分树。我们考虑怎么把两个子树的点分树合并成一个大的点分树。
设我们现在有 (u,v) 这条边,u 左边的连通块构成一个点分树,根为 rootu;v 右边的连通块构成一个点分树,根为 rootv,那么要得到两个连通块并起来的点分树,首先我们需要选择全局的根节点,它一定是 rootu,rootv 之一。例如我们选择了 rootu 作为全局点分树的根节点,那么删去 rootu 后 u 这一侧会分成若干连通块,不包含 u 的连通块就没有用了,剩下的部分是 u 那边的点分树和 v 那边的点分树接着合并。
于是我们发现实际上我们可以任意归并 rootu→u 路径上的点和 rootv→v 路径上的点。
现在我们考虑哪些点的度数发生了改变,发现只有 rootu,rootv,u,v 这四个点的度数可能会发生改变。具体来说,如果新的点分树的根是 rootu,那么 rootv 就会因为多了一个父亲而导致度数 +1,反之亦然;如果最终 u 在 v 的上面,那么 u 就会因为多了一个儿子而导致度数 +1。
于是我们记录状态 f(u,0/1/2,0/1/2,0/1),分别表示 deg(rootu)=0/1/≥2,deg(rootv)=0/1/≥2,以及 u 和 rootu 是否相同。转移的时候分四类情况计算度数的改变以及相应叶子结点个数的改变即可。
时间复杂度 O(n)。
F
我们首先考虑,给定一个 b,如何判定是否合法。方便起见,我们设 s0=P,sn+1=S,b0=bn+1=0,这样全局一定会有一个 i 满足 si=P,si+1=S,那么可以确定全局的总和就是 bi+bi+1。
接下来,考虑相邻的两个位置:
- 如果形如
PP
或SS
,那么可以确定一个 ai,只需检查其绝对值是否 ≤m 即可。 - 如果形如
PS
,那么这里也可以确定总和,需要满足所有PS
确定的总和不互相矛盾。 - 如果形如
SP
,那么可以发现我们不可能单独确定 ai 或 ai+1,只能确定 ai+ai+1 的值,那么只要这个的绝对值不超过 2m 即可。
现在考虑怎么计数,我们钦定位置 i 是最后一个 PS
,对前面 DP 一下,记录最后一个字符填的是 P
还是 S
即可;对于后面的,因为我们钦定了这里是最后一个 PS
,那么这些字符填的一定都是 S
,直接 check 即可。
时间复杂度 O(n2)。
卡自然溢出的方法
考虑 ai=popcount(i)mod2 这个字符串。
假设选手给出 base 是 P ,随机加权函数为 f:{0,1}→[1,P−1]∩Z,那么一个字符串的哈希值是
当然,要对 264 取模。
我们声称:对足够大(但没有那么大)的 k,有 h(a0⋯2k−1)≡h(a2k⋯2k+1−1)(mod264)。
或者说,h(a0⋯2k)≡h(not a0⋯2k)(mod264)。
把二者作差,我们得到
考虑后面那个式子,记 Tk=P0−P1−P2+P3−P4+P5+P6−P7+⋯+(−1)kP2k−1。
经典地,我们知道 morse 序列有一种生成方式是把所有项取反之后拼接在后面,这意味着
于是我们有 Tk=∏k−1i=0(1−P2i)。进一步,注意到
注意 2∣(1+P2j),因为 P 是奇数。于是有 2i+1∣1−P2i,进而有
我们只需要取 k 满足 k(k+1)/2≥64 即可。这和 f,P 具体是什么都没有关系。
这样的最小的 k 是 11,也就是说,我们可以用两个长度为 211=2048 的串来卡掉使用 ull
的自然溢出。
JOISC2017 Day2T3
相当于如果 [i+1,j−1] 之间的所有数都 <min(Li,Lj),我们就连一条边 (i,j),求这张图上两个点之间的最短路。显然这样的区间要么包含要么不交,而且只有 O(N) 个(不超过 2N 个)。
等等,要么包含要么不交,这不是我们平面图吗???补成三角剖分然后直接启动分治就是 O(NlogN) 了。
我们考虑这张图长什么样子,可以把他的边画成一棵树的形式:
比如这个图就是先连上 1-10-13-15
,然后连 1-2-9-10 | 10-11-13 | 13-14-15
,依次类推。
那么得到这个树的结构之后,一个点 u 子树内的点如果想到子树外的点,必须经过 u 的左右两端点之一。所以我们只需要算出每个点的左右两端点到其父亲节点的左右两端点的最短路,这样就可以一路乘上 2×2 的矩阵转移过去。最后如果要算 x→y 的答案,我们就先跳到 x,y 的 LCA 前面一步,然后算一下这个环上的距离。
先跳到 LCA 前面一步,然后算环上的距离。
图中的日文翻译:当然,这两个点的父亲节点需要确实存在。
由于本题中边权均为 1,所以我们只需要预处理 Li,j,Ri,j 表示从 i 开始走 2j 步能到达的最左和最右的点,那么只要范围不覆盖另一边的点就说明还走不到 LCA,我们从两边分别开始跳,直到范围能覆盖另一个点的时候停下。
Luogu10591 异或图
考虑钦定一个连通块形态(一共只有 Bell(n)≤21147 种),计算符合这个连通块形态的图的个数。也就是说,我们钦定一个点集的划分 S1,S2,⋯,Sk,要求 Si 内部无所谓,但不同的 Si,Sj(i≠j)之间的点一定两两不连通。那么这相当于在不同连通块之间的边都不能选。设这样的边构成集合 E,我们把每张图的边集和 E 求交,计算异或和为 ∅ 的方案数即可。于是我们可以在 O(s) 时间内计算方案数。
现在考虑给每个连通块形态赋一个容斥系数,使得连通图的容斥系数和为 1,非连通图的容斥系数和为 0。
设 C(E) 为钦定了边集 E 不能连,即连边集合为 {(i,j)|1≤i<j≤n}−E 时的连通块形态,则钦定的划分 P 的容斥系数应当为 ∑C(E)=P(−1)|E|,这就是 (−1)k−1(k−1)!,其中 k 为连通块个数。详细的推导可以参考 https://www.luogu.com.cn/article/i68r0j43。
综上,我们在 O(Bell(n)×s×n2) 的时间内解决了本题。
AGC052D
考虑原序列的 LIS 为 L,显然分出的两个子序列,LIS 较大的那一个至少为 ⌈L2⌉。
如果 L 是偶数,我们算出 f(i) 表示以 i 结尾的 LIS 长度,然后把 f(i)≤L2 的都放进第一个子序列。那么第一个子序列的 LIS 长度恰好为 L2,考虑第二个子序列,发现其 LIS 长度也恰好是 L2(因为一个上升子序列的 f 一定是严格递增的,而第二个子序列中所有的 f 都在 (L2,L] 之间,所以 LIS ≤L2;另一方面显然全局 LIS 的后一半都在这个子序列内,所以可以取到)。
现在考虑 L 是奇数怎么做。设 L=2K+1,那么两个子序列的 LIS 长度需要至少为 K+1。于是任取一个长度为 2K+1 的 LIS,这两个子序列中至少有一个得包含至少一个不在这个 LIS 中的数。
那么我们只要能选出一个长度为 2K+1 的 LIS,并找到一个不在这个 LIS 中的数,满足存在一个长度为 K+1 的上升子序列包含它,设这个数是 x,我们做如下的构造:
- 设 i1,i2,⋯,iK+1 为这个上升子序列的下标。首先把 i1,i2,⋯,iK+1 都放进第一个子序列。
- 接下来对于某个 j,如果 f(j)(以 j 结尾的最长上升子序列长度)和某个 f(ip)(1≤p≤K+1)都不同,或者 f(j)=f(x) 且 j≠x,我们就把他放在第二个子序列;否则,我们把它放进第一个子序列。
现在考虑第一个子序列,其 LIS 长度一定 ≥K+1;由于其中的 f 值只有 K+1 种,因此一定凑不出来长度为 K+2 的上升子序列,于是其 LIS 长度恰为 K+1。考虑第二个子序列,由于全局 LIS 中剩下的 K+1 个数都在这里面,因此其 LIS 长度也至少为 K+1;同时这里的 f 值也只剩下 K+1 种,因此构造成立。
现在考虑怎么判断这个条件是否成立,那么相当于除去 LIS 里面的 2K+1 个数之外,还得至少存在一个数,包含它的最长上升子序列长度 ≥K+1。
我们算出 wi 表示包含下标 i 的最长上升子序列长度,那么只要 wi≥K+1 的数至少有 2K+2 个,就能找到一种方案;否则一定无解。对于 w 的计算,算出 fi,gi 表示以 i 结尾、开头的 LIS 长度,则 wi=fi+gi−1。
AGC052E
把 A/B/C
看成 0,1,2,我们把一个字符串看成一个序列 A,满足 Ai≡Si(mod3),且 |Ai−Ai+1|=1。显然只要确定了 A1,那么序列 A 是唯一确定的。例如 ABACBAB
就可以看成序列 0,1,0,−1,−2,−3,−2。
那么一次操作就相当于,选择一个 p,把 Ap←Ap±2,使得序列仍然满足 ∀i,|Ai−Ai+1|=1。
那么首先,需要 Ai≡Bi(mod2),且操作数有下界 ∑|Ai−Bi|2。下面我们证明可以取到这个下界。
我们考虑证明,只要 ∑|Ai−Bi|>0,那么一定存在一种操作,使得我们能把 ∑|Ai−Bi| 减少 2。
我们选取 Ai>Bi 的 i 中,Ai 最大的那个,那么操作这个 Ai←Ai−2 一定合法。如果不合法,说明一定是 Ai 前后存在一个大于他的。不妨设 Ai−1=Ai+1,发现此时由于 Bi−1≤Bi+1,因此 Ai−1>Bi−1,而 Ai−1>Ai,与 Ai 最大矛盾。
不妨设 A1=0,现在只需要选取 B1 满足 B1≡T1(mod3),B1≡A1(mod2),最小化 ∑|Ai−Bi|。由于确定 B1 后,每个 Bi 都可以写成 B1+Xi 的形式,其中 Xi 是定值。
那么就是需要选取 B1,最小化 ∑|Ai−Xi−B1|。这是一个经典问题,我们应当找到 Ai−Xi 的中位数,然后让 Bi 尽量接近这个中位数。由于值域为 O(N) 可以在线性时间内排序,故本题可以在 O(N) 时间内解决。
LOJ3693
把能够互相到达的蚂蚁和方糖之间连边,那么相当于要求一个最大流。考虑钦定左部点流量 fi,那么由 Hall 定理只需要任意集合 S,其 ∑i∈Sfi≤∑j∈N(S)Aj。注意每个左部点 x 都连向右侧的一个区间 [lx,rx] 内的点。
显然集合最严的情况应该取到一个区间。
二分图匹配有拟阵性质,所以我们从前往后扫,每次给 fi 放一个他理论上能取到的最大值,就能得到最优解。
考虑怎么求 fi 的最大值。
我们求出当前限制最严的后缀 p,也就是 S={p,p+1,⋯,i} 的时候 ∑p∈N(S)Ap−∑i∈Sfi 最小。
设这个最小值为 suf,[max(ri+1,li+1),ri+1] 之间的蚂蚁权值之和为 Bi+1,[li+1,ri+1] 之间的蚂蚁权值之和为 Ci+1,那么每次我们先计算 f,然后重新计算 suf。即
发现一次操作是对 C 做区间修改,不过我们可以差分成单点修改。
具体来说,我们可以写出以下代码:
ll sum=0,ans=0,C=0;
for(int i=1;i<=n;i++){
C+=D[i],suf+=B[i],suf=min(suf,C);
if(suf<A[i])ans+=suf,suf=0;
else suf-=A[i],ans+=A[i];
}
我们考虑,只要有一个 chkmin
或者 chkmax
被执行过,那么后面的过程就和 suf 的初值无关了。
于是我们可以使用单侧递归线段树来维护,做到 O(nlog2n)。
QOJ4401
我们考虑选 K 个点,然后求出这 K 个点在两棵树上的虚树上每条边的边权。
考虑直接在第一棵树上按 BFS 序取前 K 个点,考虑每次加一个点,两边的虚树会发生什么变化。
由于我们是在第一棵树上按照 BFS 序取点,所以每次加的都是叶子,只会恰好增加一条边,我们随便询问这个新增的节点和前面的任意节点即可知道这条边的边权。
对于第二棵树,增加一个点之后,虚树上最多多出来两个点,但不管怎样,我们都可以通过一次询问知道新增的边的边权。于是这个题就做完了。
考虑怎么维护,第一棵树的维护是简单的,相当于单点修改边权,查询链上和。对于第二课树,我们考虑直接维护原树结构,把一条虚树边的边权放到靠下的节点上,那么也是单点修改,查询链上和。
时间复杂度 O(N+(K+T)logN)。
UOJ822
有一个 O(n3) 的 DP:dpu,i,j 表示 u 子树内,u 所在的连通块的最大深度不超过 i,直径不超过 j 的最小代价。转移是容易的;可以使用长链剖分优化至 O(n2)。这部分类似于 AT_mujin2017_pc_d
我们考虑最优解中的一条边,发现只有在这条边的两个端点分别为各自连通块内直径的端点时,才有可能成为最优解;否则,连上这条边后,总的直径长度之和不会变大。
于是有简单 O(n2) DP:dpu,i 表示 u 子树内,和 u 距离最远的一个点的距离为 i,且钦定这个连通块的直径不超过 i 的最小代价。转移的时候,需要做一个 min+ 卷积的形式来转移。
我们考虑拿出以 u 为根的长链,在最优解中,这条长链会被划分为若干部分。对于每一部分,都要求长链的两个端点都是直径的端点(除了第一部分和最后一部分)。这要求,要么其直径就恰好是这条长链,要么这段长链上有偶数条边,且直径从最中间的那个点向外延伸。第一条和最后一条长链除外:他们只要求靠下或靠上的那个点的确是直径的端点。
对于第一种情况,设 fu 表示 u 子树内的答案,设 su,i 表示 u 子树内,割掉所有和 u 距离为 i 的边之后,下面那些子树的 f 之和,与这些边的边权之和。这里 s 中没有计算 u 所在连通块的贡献。那么把长链拿出来后,设 s′k,p 表示长链上的第 k 个点,割掉其轻子树内和 k 距离为 p 的边,的贡献之和。那么第一种在 i→j 之间的转移的贡献可以写成 val(i,j)=∑s′k,min(k−i,j−k) 的形式。
注意这里 s′ 的状态数总量是 O(n) 的,这个就是普通长剖的复杂度分析。
扫描线扫 i,考虑 i→j 之间一个 s′k 的贡献在 i 变为 i−1 时有什么变化。那么就是第二维 min(k−i,j−k) 变成了 min(k−i+1,j−k)。当 k−i≥j−k 时,这个没有变化;当 k−i<j−k 时,只有 k−i<lenk 的才会发生变化,这里 lenk 表示长链上第 k 个点的轻子树长链 length 的最大值。
那么每次我们暴力枚举所有 k−i<lenk 的 k,用它来更新所有 val(i,j)+fj+1 的值,是一个区间加的形式。使用线段树维护,可以做到 O(nlogn)。
对于第二种,我们设 gu,i,表示 u 子树内,钦定 u 所在的连通块的最浅点为 u 的 i 级祖先的最小代价。那么这里转移的时候,只需要讨论直径的中心在哪里:u,或者某条 u→v 的边上,或者某个儿子 v 的子树内。对于第一种,有 gu,i←su,i。对于第二种,如果直径在 u→v 这条边的中点上,则 gu,i←sv,i+∑p∈son(u),p≠vsp,i−1。对于第三种,有 gu,i←gv,i+1+∑p∈son(u)sp,i−1。
然后这里还有 D× 直径的贡献,这里我们在中心处计算即可。也就是把前两种转移的权值分别加上 D×2i 和 D×(2i+1)。
那么同理设 g′k,p 表示长链上第 k 个点,只考虑轻儿子的 gk,p,那么有 p≤lenk。
转移的时候,相当于 fi←gk,p+f2k−i+1+val′i,2k−i。这里 val′ 指的是,[i,2k−i] 这段区间内的点 x,除了 k 之外,也不能延伸出去超过 min(x−i,2k−i−x) 的长度。
对于第一段和最后一段的特殊情况,我们在这条长链的开头和结尾都添加等同于长链长度这么多个点,在转移最后一段的时候允许超出原本的长链末端,然后对于 froot 我们把他对长链前面新增的点的 f 取 min 即可。
最后我们考虑 g 怎么算,不难发现只需要算出根节点的 g,发现根节点的 g 几乎就是我们新增的那些点的 f,只不过由于限定了中心在根节点往下的位置,所以转移的区间有一些变化。
综上,本题在 O(nlogn) 时间内解决。
注意这里做的实际上是后缀加,查询全局 min,因此,我们使用并查集维护,可以做到 O(nα(n)) 或者 O(n)。
QOJ7792
考虑怎么算 u 在拓扑序 j 号位的方案数。
找到 1→u 的这条链,那么相当于选一个包含 1 的连通块,满足其大小恰好为 j−1,且包含 fau,算这个连通块的拓扑序个数,再求和。众所周知一棵树的拓扑序个数可以写成 n!∏ni=11szi,于是我们预处理 g(u,j) 表示从 u 子树内选一个包含 u 的大小为 j 的连通块,其拓扑序个数除以 j! 的总和。
那么 g 的转移就是先把各个子树做树形背包合并,最后令 g(u,j)←g(u,j)×1j。现在考虑怎么算 u 在拓扑序的第 j 位的方案数,设 1→u 路径上的点分别为 v1(=1),v2,⋯,vk(=fau),相当于要对每个 vi,在所有除了 vi+1 的子树内选取一个连通块,然后算一下 ∏1szi。
注意到 szvi 可能还和 vi+1,⋯,vk 有关,我们考虑记录 f(u,j) 表示当前 DP 到点 u,钦定后面还有 j 个点的方案数。那么要算 u 在拓扑序 j 号位的方案数,只需要一开始令 f(1,j)=1,然后一路 DP 下去,每次枚举另一边子树的 size 为 k,做转移 f(u,j)×1j×g′k→f(son,j−k) 即可。这里的 g′k 表示 g(u,⋅) 扣掉 son 这个子树之后的 DP 值。
这里直接做是 O(n3) 的,要想优化,有两个问题需要解决:一是怎么把最外层的枚举 j 去掉,第二是如何保证每次从 g(u,⋅) 里面扣掉一个子树的 g(son,⋅) 的复杂度正确。
对于第一个问题,考虑到题目只需要求 ∑jwu,jbj,其中 wu,j 表示 u 在拓扑序第 j 位的方案数,我们考虑,类似于转置原理,一开始把 f(1,j) 设置为 bj×(j−1)!,那么这样算出来的值就是正确的 ∑jwu,jbj。
对于第二个问题,分析一下每次扣掉一个子树的复杂度。考虑计算 H=FG,有
把 hi 单独提出来就得到
于是我们可以在 O(degH×degG)=O((degF−degG)×degG) 的时间内计算除法。
那么我们现在我们得到了一个 O(∑(size(u)−size(v))×size(v)) 这样的复杂度。可以发现,转移 f 也是这个复杂度。类似于普通树形背包,这个不会超过 ∑u∑v1,v2∈son(u),v1≠v2size(v1)×size(v2),那相当于任意一个点对 (x,y) 只会在 LCA 处贡献一次,于是复杂度是对的。
实际上我们是可以算出 u 在拓扑序 j 号位的方案数的:直接对填数过程 DP,设 w(u,j) 表示 u 的拓扑序为 j 的方案数,那么确定 1→u 填的数分别为 x1,x2,⋯,xk 之后容易用若干组合数乘积算出贡献。那么转移只需要除掉一个组合数,再乘上一个组合数即可。
UOJ671
考虑一开始 x=2k−1,然后用 k−1 次操作把 x 操作成 2k−1,接下来变成 ⌊2k−13⌋。考虑这个数,发现他会形如 01010101
或者 010101010
,取决于 k−1 的奇偶;然后我们接着操作到 2k−3,再除以 3,这样可以把一个数的操作次数构造到 O(log2V) 级别。具体来说,k=128 时这个操作次数为 2144。
考虑 opi=2 怎么做,直接做是 O(nlognlogV+qlogn),但是你可以这样做,线段树每个点直接维护一个长度为 128 的数组 cnt,cnti 表示第 i 位为 1 的个数,然后再维护 128 个标记即可。
这 128 个标记可以压成一个 __int128
。
那我们考虑 opi=1 怎么做,相当于每次需要找到所有 ≥1 的数,你考虑维护个平衡树,里面只维护当前 ≥1 的数,把 =0 的直接删掉,那么每次把这个区间 [l,r] 分裂出来之后,我们需要用 O(r−l) 不带 log 的时间来做完这个操作。
发现建平衡树是可以线性的:我们随机完键值之后跑一遍笛卡尔树即可,那么就可以在 O(r−l+logn) 的复杂度内完成这些操作,于是复杂度是 O(nlogV+qlogn)。
那对于两种操作都有的,虽然两个结合起来操作次数是 log2V 级别,但是无论如何除法操作都是 O(logV) 次。我们还是维护平衡树,里面只存 ≥1 的数。那还是考虑每次对于 &
操作我们打标记,对于除法我们分裂出中间这些数之后暴力下传标记,最后重新建出这棵树。
这里注意 pushdown
和 pushup
的时间复杂度都是 O(logV),我们需要把他缩减到均摊 O(1)。首先 pushdown
容易优化,因为我们这里只需要求出每个节点具体的值,因此不需要考虑标记对 cnt 的影响,于是 pushdown
就是 O(1);考虑怎么 pushup
,发现倒闭了。。。
回忆一下 Xor Master 那个题,发现类似那题,我们把 cnt 数组竖向存储,然后 pushup 的时候用位运算模拟加法,这样复杂度是 T(n)=2T(n/2)+O(logn)=O(n)。于是我们就可以线性 rebuild 这个子树,然后就全对了。
实际上把平衡树改成线段树也是对的,复杂度是 O(nlogV+qlogn)。考虑一个节点被访问的次数,发现他被访问一次这里面的所有数就都会除以 2,所以每个节点至多被访问 logV 次,总复杂度 O(nlogV+qlogn)。
UOJ703
考虑我们这样操作:
a b c -> a b^a c -> a b^a c^b^a -> a b c^b^a -> a b c^a
这样可以对三个数 a1,a2,a3 做到不改变 a2 的前提下令 a3←a1⊕a3。
现在考虑 k 个数 a1,a2,⋯,ak,考虑如果我们已经有了 ak−1←ak−1⊕a1 且不改变其他数的操作,我们重复上面三个数的操作就可以做到 ak←ak⊕a1,且不改变其他数。
于是我们现在可以对任意 i<j,令 aj←aj⊕ai。现在相当于 ai 的取值是前 i−1 个数的线性基 ⊕ai。
如果 ai 在前 i−1 个数的线性基内,那么他的取值就是这个线性基内部的任意数。从后往前考虑,如果一个数在前面的线性基内,一定是贪心选中;除去这些数之后,剩下的数只有 logV 个。
考虑类似普通 LIS,我们维护一个数组 f,fi 表示长为 i 的 LIS 开头的 max,那么每次如果加入一个取值集合为 S 的数,相当于对每个 fi,用 S 中 ≤fi−1 的最大的数来更新 fi。
考虑按照那些不在前面的线性基内部的数的位置(称这些为关键位置)把序列分段,先考虑最后一段,发现此时只会改变 f 末尾的一个数。然后插入最后一个关键数,因为他的取值是后面的子集,他也只能把 f 的末尾加一个数。我们考虑再插入一段,f 前面的若干个数是不可能动了,最后一个数可能会变大,还会在末尾添加一个数。接下来的每次操作,都只会影响 f 的后两个位置。类似地推下去,我们的操作只会影响到 f 的后 logV 个位置。
于是我们只需要维护 f 的后 logV 个位置。
注意到一段的贡献可以快速算出来,总复杂度是 O(nlogV+log3V) 左右。
- 这里有一个问题:给定 x,怎么求线性基 S 中满足 x⊕y≤k 的最大的 x⊕y:枚举第一个 x⊕y 和 k 不同的位,然后直接做就行了。一个更聪明一点的办法是,先把线性基简化至每个数最高位不同,而且每个数在这个最高位上都是唯一的一个 1 的形式(这称为线性基的最小基),然后先求出 x⊕y 的最小值,接下来再依次考虑每个数 y,只要 x⊕y≤k 就令 x←x⊕y。注意,此时一定有 x⊕y>x。
ARC139E
首先考虑怎么求 L。
当 H,W 均为偶数时,发现 L 可以取到上界 12HW,且方案只有两种:取黑白染色后的所有黑点或白点。
考虑 H 为偶数,W 为奇数,那么此时每一行最多取 W−12 个,而我们也可以取到这个上界,于是 L=(W−1)H2 。考虑方案数怎么算,则只需要让每一行都恰好取到 W−12 个。
考虑此时这一行的形态,W 个点的环放进去 W−12 个点,那么剩下 W+12 个点被分成 W−12 个非空的段,于是一定恰好有一段有两个点。钦定这两个点的位置之后,剩下的点的放置方法是唯一的。于是方案数是 W。
考虑相邻两行之间的约束,不难发现这和上一行具体是什么无关。设 xi 表示第 i 行选的位置,即我们让第 i 行的第 xi,(xi+1)modW 两个位置空着,发现不管怎样,我们只能有 xi+1=xi+1 或 xi+1=xi−1(运算都在 modW 意义下进行),注意这里第一行和最后一行之间也有约束,相当于要算
考虑这个咋算,发现有一个办法是我们枚举终点具体在哪,如果是 (H,i×W),方案数为 (HH+iW2),于是我们可以 O(HW) 枚举终点,然后 O(1) 算组合数;另一方面借助 poly 可以做到 O(WlogWlogH)。
综合一下,当 W>105 时必有 H≤105,此时答案就是 (HH/2);否则我们可以使用 poly。
最后还剩下 W,H 均为奇数的情况。同理,答案不会超过 min(H(W−1)2,W(H−1)2)。
不妨设 H≥W,此时 W(H−1)2≥H(W−1)2,我们证明答案就是 H(W−1)2。
同理,我们设 xi 表示第 i 行选取的位置,需要有 |xi+1−xi|=1。此时,由于 H≥W,我们总可以调整 x 使得 xH−1=W−1,于是就得到了一个符合条件的方案。那么计算方案数也是类似的。
综上,本题在 O(HlogHlogW) 时间内解决。
AGC052C
考虑分析什么样的序列是合法的。首先,总和不能是 P 的倍数;接下来我们考虑每次尝试填入一个数,如果会导致前缀和 modP=0,我们就换一个数填入。这样每次我们填入当前出现次数最多的数,只要不存在一个数的出现次数 >N2,就一定能够填完。
如果存在一个数的出现次数 >N2,设其为 x,我们把所有数都乘上 x−1modP(注意 P 是质数,因此存在逆元),那么就转化为 x=1 的情形。设有 k 个 1,我们考虑每次前缀和经过某个 i×P 的时候,都需要一个 >1 的数来跨过去,所以总和最多为 (n−k)×P+P−1。只要总和不超过这个数,发现只要执行上面那个过程就可以得到一组解了。
最后我们考虑计数,考虑算不合法的方案数。首先计算总和为 P 的倍数的方案数,设 fi 表示 i 个数和为 P 的倍数的方案数,我们让前 i−1 个数任意填,只要和不是 P 的倍数,最后一个数总有唯一的方案使得总共的和是 P 的倍数;否则一定无解。于是 fi=(P−1)i−1−fi−1。
接下来考虑总和不是 P 的倍数的方案数,我们钦定 1 出现的次数 >N2,再钦定其他数的总和爆掉。
设 dp(i,j) 表示 i 个 >1 的数,其 ∑P−ai+P−1=j 的方案数,考虑什么时候不合法。
那么只需要 N−i>j 且 i<N2,这个状态就不合法。不过可能会多算一些总和为 P 的倍数的情况,此时相当于 N−i−j≡1(modP),把这些情况判掉就好了。使用前缀和优化 DP 即可做到 O(N2)。
JOISC 2024 Day3 T1 : Card Collection
首先,注意到对于一组询问,我们只需要关注每个数与 (Tj,Wj) 的相对大小关系。这一共有 9 种情况,于是我们直接做区间 DP,设一个形如 f(l,r,0/1/2,0/1/2) 的状态,即可得到 O(N3M) 的做法;进一步使用 bitset 优化可以做到 O(N3Mw),但是无法通过(甚至 N=2000,M=10 可能都无法通过)。
我们考虑想要得到一个 (Tj,Wj),不论怎样,操作总是由两部分构成:
- 用区间 [l,r] 造出来一个 (Tj,Wj)。
- 然后无伤通关 [1,l−1] 和 [r+1,N],把这个 (Tj,Wj) 保持到最后。
考虑能够保持到最后的条件。我们发现,如果某一侧全部都形如 (≥Tj,<Wj),或者全部都形如 (≤Tj,>Wj),那么一定是不可能保持到最后的。否则,我们证明一定可以。
如果不是全部形如以上两种形态,那么相当于至少存在一个 (≤Tj,≤Wj) 或 (≥Tj,≥Wj)。我们在这一侧一直取 min 或取 max 就可以保留这个 (≤Tj,≤Wj) 或 (≥Tj,≥Wj)。最后,进行一次取 min 或取 max 即可。
现在我们考虑,用区间 [l,r] 造出了 (Tj,Wj),如果是直接得到的也就是 l=r,这种情况是好判断的。
如果这个过程也经历了至少一次合并,我们设最后一步合并的两个区间为 [l,m]+[m+1,r],那么这两个区间的结果有四种可能:
- (Tj,≤Wj)+(≤Tj,Wj)
- (Tj,≥Wj)+(≥Tj,Wj)
- (≤Tj,Wj)+(Tj,≤Wj)
- (≥Tj,Wj)+(Tj,≥Wj)
不管哪种情况,我们发现总是可以将操作改为:先分别合并出 [l,m] 和 [m+1,r],然后把 [1,l−1] 得到的 (≤Tj,≤Wj) 或 (≥Tj,≥Wj) 合并到 [l,m] 里面,并保持形态不变;把 [r+1,N] 得到的 (≤Tj,≤Wj) 或 (≥Tj,≥Wj) 合并到 [m+1,r] 里面,最后再把 [1,m],[m+1,N] 合并。
于是,我们只需要考虑最后一步操作形如 [1,m]+[m+1,N] 的情形。
现在,我们可以把原问题转化为 O(N) 次判断如下的问题:
- 能否用一个序列造出一个形如 (Tj,≤Wj) 的数。
由对称性,其他情况也是类似的。
我们考虑想要造出一个 (Tj,≤Wj),发现如果它是由两个均不为 (Tj,≤Wj) 的卡牌合并而来,那么唯一的情况就只有 (≥Tj,≤Wj)+(Tj,>Wj)。
我们先来考虑,如何一个序列能否造出 (≥Tj,≤Wj)。类似地,这个过程也由两部分组成:
- 用区间 [l,r] 造出来一个 (≥Tj,≤Wj)。
- 然后无伤通关 [1,l−1] 和 [r+1,N],把这个 (≥Tj,≤Wj) 保持到最后。
这时我们发现,(≥Tj,≤Wj) 不可能由两个均不为 (≥Tj,≤Wj) 的卡牌合并而来。于是,唯一的情况只有:原序列中存在至少一个能保留到最后的 (≥Tj,≤Wj)。
现在考虑一个 (≥Tj,≤Wj) 能保留到最后的条件。发现如果某一侧全都是 (<Tj,>Wj) 那么肯定无解;否则这一侧必须存在 (≥Tj,∗) 或者 (∗,≤Wj)(其中 ∗ 表示任取)。对于这种情况,同理我们也可以先在这一边一直取 min 或 max 保留这个卡牌,最后进行一次操作留下 (<Tj,>Wj)。
现在,我们可以在 O(N) 时间内判断一个长为 N 的序列能否造出一个形如 (≥Tj,≤Wj) 的卡牌。
回到原问题,考虑如何造出一个 (Tj,≤Wj) 的卡牌。下面我们证明,这等价于:
- 存在至少一个形如 (Tj,∗) 的卡牌,且这个序列能造出 (≥Tj,≤Wj) 的卡牌。
首先,必要性是显然的。对于充分性,假设我们使用了某个 k 满足 (Sk,Vk)(这里 S,V 是给定的初始序列)一开始就形如 (≥Tj,≤Wj),且两侧均存在至少一个 (≥Tj,∗) 或 (∗,≤Wj)。
设 p 是序列中任意一个满足 Sp=Tj 的卡牌,分以下两种情况:
- p=k。这种情况下,我们类似地在左右先进行操作,可以发现总是能保留这个 (Tj,≤Wj)。
- p≠k。不妨设 p<k,那么我们在右侧正常操作,对于左侧,我们无脑保留 Tj(在不关心第二维的情况下,这当然是可以做到的),然后最后把留下来的 (Tj,∗) 和 (≥Tj,≤Wj) 进行一次合并即可。
综上命题得证。
于是,我们可以在 O(N) 的时间内判断一个长为 N 的序列能否造出一个形如 (Tj,≤Wj) 的卡牌。对于每组询问我们都需要做 N 遍上述过程,于是总的时间复杂度为 O(N2M)。
下面就是我们熟悉的部分了!相信在得到最后的条件后,作为 CN OIer 你的内心已经蠢蠢欲动了(雾
还是不妨设最后一步合并形如 (Tj,≤Wj)+(≤Tj,Wj)。
我们找到第一个形如 (Tj,∗) 的卡牌 x,以及第一个形如 (≥Tj,≤Wj) 且左侧存在至少一个 (≥Tj,∗) 或 (∗,≤Wj),或者左侧没有任何卡牌的卡牌 y。找到 y 右侧第一个形如 (≥Tj,∗) 或 (∗,≤Wj) 的卡牌 z,那么能合成 (Tj,≤Wj) 的前缀只有 [1,y](此时还需要 x≤y),以及所有的 [1,p],其中 p≥max(x,z)。
同理我们也可以找到所有的后缀使得其能构造出 (≤Tj,Wj)。现在相当于给出 p1,q1,p2,q2,判断是否存在一个 i 满足 i∈{p1}∪[q1,n],i+1∈{p2}∪[1,p1]。分四种情况讨论即可。
对于找到这个前缀,可以发现只需要每组询问只需要做一次「查询 p 之后第一个 (≥u,≤v) 的卡牌的位置」这样的询问,还有两次查询 (≥u,∗) 和 (∗,≤v) 的询问。后两个都容易通过线段树二分或 ST 表解决,对于第一个,我们把询问按 u 排序后扫描线,那么只需要线段树二分同时维护单点修改即可。
最后还需要考虑直接拿着序列中一个 (Tj,Wj) 走到最后的情形。考虑把询问记忆化,每次枚举序列中的所有 (Tj,Wj) 并一一判断其前后是否分别都有一个 (≥Tj,≥Wj) 或 (≤Tj,≤Wj)。注意这并不是三维偏序,因为我们只需要判断存在性。同理按照 T 从小到大插入,每次线段树二分即可。
综上,本题在 O((N+M)logN) 时间内解决。带有 8 倍常数,因为有四种情况且每次都要对前缀后缀同时算。
CF1930H
这个题相当于要用两个排列的 5 个区间来覆盖一条链的链补。
首先 O(logn) 自然是简单的:一条链重链剖分后在 DFS 序上只会是 O(logn) 个区间。
我们考虑取一个 DFS 序,画一下一个极端的情况:一条长链,每个点左右各往下挂一个点,然后询问根到叶子。发现左侧的点的 DFS 序会被分割开,右侧则保持着一段后缀。但是在出栈序中,左侧的点是一段区间。
于是我们可以拆成三个区间:入栈序一个前缀,出栈序的一个区间,入栈序的一个后缀。
那么对于一般的情况,也可以类似地拆成五个区间。
2024.6.24
A
考虑建一个网络流图,原图一条边 (u,v) 看作两个点,分别表示 u→v,v→u 的徽章。把每个点相邻的边中交换时间小的入边连向时间大出边的,表示一个徽章可以顺着这个点移动到那一次交换。每条边的流量都是 1。从源点 s 连向每个点 i,其中一条容量为 1 费用为 ai,另一条容量 ∞ 费用 0,意思是每种徽章只算一次;从 1 的每条邻边的入边代表的点连到汇点 t,容量 1 费用 0。这样得到的最大费用流(不一定是最大流)就是答案。
这样建出的边数是 O(∑deg2i)=O(mk),当然我们可以优化建图做到 O(m) 条边,但这样会导致新的边的容量应该设为 ∞;而我们之前建出的图是一个每条边容量均为 1 的 DAG,它具有更好的性质,所以我们不优化。
现在我们考虑怎么判断点集 S 合法,设图中连向汇点 t 的点集为 T,一个点集 S 合法,需要满足它能向 T 中的 |S| 个点连出一个不相交路径组。考虑给图中的每条边赋 [1,p−1] 的随机边权,设 e(x,y) 表示图中 x→y 的所有路径上边权乘积之和,那么 A→B 如果存在不相交路径组(|A|=|B|),我们列一下 LGV 引理那个行列式,由于我们是随机赋的边权,这个行列式为 0 的概率不超过 np。
所以我们可以用线性无关来判断 S 是否合法。设 deg1=d,那么相当于有 |S| 个 d 维向量,需要判断能否取出 |S| 维,使得它们线性无关。显然,如果他们整体线性相关,那么一定不存在;否则如果整体线性无关,也一定存在解。(直接考虑把他们插入线性基里面得到的那些唯一的最高位,把这些位取出来就好了)
于是我们维护线性基即可。每次插入时,如果没办法插入,就尝试把线性基内最小的和他相关的元素删除。
时间复杂度 O((n+m+q)k2)。
B
直接维护有理分式即可。每次需要把一个形如
的东西分解为
这个可以在 O(k) 时间内完成。于是我们 O(q2) 维护有理分式,配合 O(P1/3)−O(1) 的光速幂即可。
C
直接跑 Boruvka 配合数据结构即可做到 O(nlog2n)。
进一步发现这个模型实际上还可以扩展到 Prim 上,于是可以做到 O(nlogn)。
2024.6.25
A
做了 2.5h 才过大样例,然后卡了 1h 的常数...
首先 T 不合法只需要 T 的最小整周期 T0,满足 T 某一次出现后面紧跟着 T0。
既然要对本质不同子串计数,我们肯定要往 SAM endpos 之类的东西上扯。那就考虑,这相当于 T 的两次出现位置之差是 |T| 的约数。通过弱周期引理,对整周期长度是否 ≤|T|2 讨论,可以证明:SAM 一个节点上只有相邻的 endpos 之差会造成影响。
于是我们启发式合并维护 endpos 之差,然后每次枚举本质不同的 endpos 之差(注意这只有 O(√n) 种,因为其总和为 n)。这里我们只需要枚举差 ≤lenu 的,这种情况下,可以证明这些差之中一定不会存在一对 x<y 满足 x∣y。这是因为,由于 x,y 均为 T 的周期,考虑相差 y 的那一次出现,发现这里在相差 x 之后一定会先出现一次,所以 y 就不会成为相邻 endpos 之差。
类似地可以发现,如果我们存在 i∈(lenfa(u),lenu] 满足这个集合中存在 x∣i,y∣i,x≠y,那么 gcd(x,y) 也一定在这个集合内,发现这样就有一对整除关系,导出矛盾。
所以我们直接枚举集合中的每个数,算一下它在 (lenfa(u),lenu] 中的倍数个数,减掉即可。这样就能恰好不重不漏地统计所有贡献。时间复杂度是 O(nlog2n+n√n)。
这里似乎还有更深刻的东西,比如说除去那些本身就是自己出现两遍的串之后,剩下的造成贡献的周期长度只会有 O(logn) 个左右,因为每次周期变化的时候差不多都会翻倍。
所以如果能快速找到这些位置,大概能做到 O(nlog2n)。但是我并不是很会啊。
std 是利用了 runs 以及幂串的性质,做到了 O(nlogn) 甚至 O(n),这我哪会啊
B
随便点减边一下就完事了
C
建出括号树,那么只有兄弟括号和父子括号可以交换,且方案数一定是 2。所以我们容易写出一个 O(n2) 递推,看上去就存在整式递推。实际上,ans=5n−2(n+2)(2n−1)。幽默
CF1672I
看到题目就很容易想一个贪心:每次选取 |i−pi| 最小的一个 i,将其删去。那么答案就是过程中选取的 i,他们的 |i−pi| 的最大值。实际上,这个贪心是对的。
证明:由于 i 是当前 |i−pi| 最小的一个,不妨设 i≤pi,对于任意的 j≠i,如果 j<i,可以发现 |pj−j| 不会变大;如果 j>i,那么必有 pj>pi(否则 |j−pj|<|i−pi|),所以 |pj−j| 保持不变。于是这样操作之后所有其他 j 的 |pj−j| 都不会变大,故操作 i 一定是最优的。
现在只需要考虑如何维护这个过程。如果直接用数据结构维护整个序列,可能不太能做啊。
我们把所有 i 分成 i≤pi 和 i>pi 两类,对于第一类,我们需要最小化 pi−i 的值。此时如果 i<j,pi>pj,那么必有 pi−i>pj−j,于是我们只需要维护所有后缀最小值;同理,对于另一类只需要维护所有前缀最大值。
同时可以发现,我们一定不会出现两类数之间的交换(也就是说,不会出现某个 i≤pi 在操作后变成了 i>pi,或者 i>pi 变成了 i≤pi 的情况)。这是因为,每次 |i−pi| 最多变化 1,而交界处有 pi=i,即 |i−pi|=0,此时我们一定会选择直接删掉 i。显然这也不会影响到其他 pj=j 的 j。
综上我们使用线段树维护即可,每次删数时,二分一下把新的前缀 max 和后缀 min 加入进来。由于每个数只会被加入一次,时间复杂度为 O(nlogn)。
最大势算法
求弦图的完美消除序列:依次加点,每次找一个和已经加入的点连边最多的点加进去。
如果这个点不合法,则图不为弦图;如果有多个最大值,任取一个。
题
给定一棵树和树的一个 DFS 序 p,每个点有点权 au,你需要维护两种操作:
- 给定 l,r,x,对所有 pu∈[l,r] 的 u 的点权 au←au+x。
- 给定 u,v,查询链 u→v 上的点的点权之和。
1≤n≤2×105
做法一:我们考虑 DFS 过程从 x→y 经过了哪些点:

我们给树做轻重链剖分,做一个重标号,满足以下性质:
- 一个 DFN 区间内的点可以被拆成这个重标号上的 O(logn) 个区间。
- 一条链上的点可以被拆成这个重标号上的 O(logn) 个区间。
- 对于每个点 u,如果 (u,fau) 是一条轻边,那么 u 子树内的点的标号是一个区间。
考虑每次拎出从根往下的重链,先 DFS 所有 DFN 大的轻儿子,再把这条重链标号,再 DFS 所有 DFN 小的轻儿子。这样每条重链上的点都是一个区间,前后的点也都是区间。
做法二:我们考虑查询 1→u 这条链的点权和,考虑 DFS 从 1 到 u 的过程,发现在这条链上的点恰好是所有 dep 为后缀 min 的点。单侧递归线段树维护即可。
[NOI2023] 桂花树
我们考虑 n=1 怎么做。
题目的约束相当于,对任意的 i,[1,i] 的虚树上的点都在 [1,i+k] 范围内。
考虑对于一个点,在他第一次被加入虚树的时候统计其贡献。维护插入 [1,i] 之后的虚树结构,考虑插入 i+1 会导致虚树发生什么变化。显然,虚树上首先会多出来一个 i+1 号点,然后还会额外多出来最多一个点(这种情况发生在 i+1 和前面某个点的 LCA 不在虚树上的时候)。
那么多出来的这个点肯定在 [i+2,i+1+k] 之间,设其为 p,我们相当于先把 p 插入到 [1,i] 的虚树的一条边上,然后再将 i+1 设为 p 的儿子。
考虑 DP:f(i,S) 表示当前考虑了前 i 个点,[i+1,i+k] 之间的点,当前已经在虚树上的点构成集合 S。那么插入 i+1 时,有若干种转移:
- i+1∈S,那么它的方案数已经被算过了,直接转移到 f(i+1,S−{i+1})。
- i+1∉S,但插入 i+1 时没有新增别的点,那么 i+1 只能是叶子或者在虚树的边上,有 2(|S|+i)−1 种方案,于是我们做转移 f(i,S)×(2(|S|+i)−1)→f(i+1,S)。
- 插入 i+1 时新增了点 p,同理 p 一定在虚树的边上,有转移 f(i,S)×(|S|+i−1)→f(i+1,S∪{p})。
那么对于 n>1 的情形,可以发现原树的形态对答案没有影响,唯一的区别是我们在 DP 的时候需要把后两种转移的系数分别加上 2n 和 n。综上,我们在 O(mk2k) 的时间内解决了本题。
2024.6.27
?
A
我们考虑在 trie 上走,比如 k 的最高位是 0,那我们就相当于选择一边走进去;否则我们可以选择一边的所有点,或者往两边同时走。那比如现在有 x,y 两个点,k 这一位是 1,那么可以选择同时走一边,然后选择所有点;或者同时往两边一起走。发现如果同时一起走,那么只有 (lsx,rsy) 和 (rsx,lsy) 会产生约束。
设 f(x,y) 表示从 x,y 两个点往下,最多能选多少个点;cnt(x) 表示 x 子树内的点数。那么转移有:
- k 这一位是 0,则 f(x,y)←max(f(lsx,lsy),f(rsx,rsy))
- k 这一位是 1,则 f(x,y)←max(f(lsx,rsy),cnt(lsx),cnt(rsy))+max(f(rsx,lsy),cnt(rsx),cnt(lsy))
这里如果发现 x 或者 y 是空节点,我们就直接返回 cnt(x) 或者 cnt(y)。
发现一个点的对应点是唯一的,所以状态数只有 O(nlogV)。同时每次修改只会影响到一条链上的状态,所以直接暴力维护,复杂度就是 O((n+m)logV)。
2024.6.28
A
设 f(i,j,k) 表示 i 个点,钦定 j 是根,其标号为 k 的方案数。
这里对于多个子树的 case,我们考虑钦定 1 所在的子树去转移,改一下转移乘上的组合数,就对了。
转移是一个二维卷积的形式,暴力做一下就可以做到 O(n4m);改成二维 FFT 即可做到 O(n2mlogn)。
事实上,观察到 j 这一维一直 ≤i,所以我们可以对这一维插值,做到无 FFT 的 O(n3m)。
B
先对偶一下,答案就是最小权点覆盖和 12∑ai 取 min。
考虑怎么支持区间加,我们同时维护这个最优解的方案中,选了多少个点。那么这个个数只会越来越小。
那么 pushup 的时候我们就正常合并,考虑区间加的时候会发生什么事。
发现只有在某个点处,合并的时候取的 max 发生了改变的时候,才会出现最优决策的切换。我们额外维护一个 t 表示最少需要加多少会改变这个最优解,如果改变了,就暴力递归下去再 pushup 上来。
这样可以证明复杂度是 3log 或者 4log 之类的,但是常数非常小,可以通过。
C
首先写一个优秀的拆分,变成 O(nlogn) 次斜线加,O(n) 次 2side 矩形求和。
场上写了一个 3log 的东西没写完,赛后改改就过了......
考虑按照 len 扫描线,那么一个合法的开头 [l,r] 和长度 len 对 [L,R] 的贡献是 |[L,R−len+1]∩[l,r]|。
转换一下,如果 r≥L,那么这个相当于 |[L,R]∩[l,r+len−1]|−len
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探