Cry_For_theMoon  

启动。

DEGwer's Doctoral Dissertation Cheering Contest

好魔怔的比赛。

E. Half Palindromes

先考虑单个 \(f(l,r)\) 的计算,有结论:我们一定会不断删最小的长度为 \(k\) 的前缀,满足前 \(2k+1\) 个字符是回文的。直到没有这样的 \(k\) 为止。

证明也很容易,假设我们某一步删了长度为 \(q\) 的前缀,而不是长度为 \(p\) 的前缀(其中 \(p\lt q\)),那么我们可以先删长度为 \(p\) 的,再删长度为 \(q-p\) 的。

现在考虑对每个 \(l\),求出最小的 \(p\gt l\),令 \(len=p-l\),则满足 \([l,p)\)\((p,p+len]\) 对称。那么当我们的 \(r\ge p+len\) 的时候,就会从 \(l\) 跳到 \(p\)

注意到一共有 \(O(n)\) 个这样的条件,我们可以考虑结合扫描线的过程:从小到大扫描 \(r\),对每个 \(l\),维护 \(f_l\) 表示 \(l\) 开始删,会删到哪里。那么上面的条件就等价于:当我们扫到 \(r=p+len\) 的时候,就会把所有 \(f=l\) 的位置指向为 \(f=p\)。而我们显然只关注 \(\sum f\)。因此就可以维护 \(f\) 的桶,然后每次修改对应的 \(f=l\) 的位置,直接在桶上修改即可。需要注意的一个点是,由于 \(f=p\) 的人可能在之前也已经指向了新的位置,那么我们 \(f=l\rightarrow f=p\) 的时候,就应该把 \(f=l\) 的人,指向 \(f=p\) 的人所指向的位置,需要用并查集来维护一下。

那么显然除了预处理所有 \(p\) 以外,剩余的部分可以看作线性;而 \(p\) 的处理可以考虑:预处理出每个中心能拓展出的最长长度,然后就是利用 dsu 做一个离线的区间 chkmin 的事情,那么如果用 manacher 的话,这部分也能线性。当然其实是带了一个 dsu 的复杂度的。

代码

F. K-Medians Clustering

我趣,这不是我们 XXII OpenCup XiAn 的 E 题吗?怎么变成计数出出来了?

那么直接快进到:我们对把 \(c_i\) 排序后(\(c_0=0,c_{k+1}=m+1\))排序后,分别来考虑每个 \((c_i,c_{i+1})\) 之间的段。

然后不妨来算不合法的情况,因为它是一个类似绝对众数状物的东西:就是有一段 \((c_i,c_{i+1})\) ,这里面的数的出现次数 \(cnt\),减去剩下的 \((n-k-cnt)\),依旧大于 \(i\)。显然这样的 \(i\) 若存在,则唯一。

那么不妨先来枚举它在哪一段 \((c_i,c_{i+1})\) 内,然后一个常规的想法是,枚举这一段内所有数的出现总次数 \(x\)\(x\) 应当有一个可以计算的下界 \(m\)。然后把剩下的 \(n-k-cnt\) 分配给这一段以外的所有数。那么就是两个插板的组合数相乘的一个东西。发现我们要做 \(k\) 次,每次是 \(O(n)\) 的,这样我们得到的是一个 \(O(nk)\) 的做法。

注意到这个其实是范德蒙德卷积,带了一个下指标的情况。它看起来给人一种很难优化的感觉。

事实上这一部分没有什么简洁的通项了,但是我们有一个类似于“转置”的手法,把这个 \(O(n)\) 的式子变成一个 \(O(c_{i+1}-c_{i})\) 的式子,然后再利用 \(\sum (c_{i+1}-c_{i})=O(m)\) 的事实把这个题在 \(O(n+m+k)\) 的时间内解决。

大体思路就是,我们并不枚举出现次数 \(x\ge m\),而是说,我们因为不关注多重集内数的顺序,所以可以把多重集看作一个递增的序列,然后我们不妨枚举 \((c_i,c_{i+1})\) 内的数里,第 \(m\) 个数的值 \(y\),那么也就相当于,前 \(m-1\) 个数可以是 \((ci,y]\) 里的,剩下的 \(n-m-k\) 个数,既可以是 \([y,c_{i+1})\) 里的,也可以是 \((ci,c_{i+1})\) 区间以外的。那么这次还是插板,但是枚举量就讲到了 \(c_{i+1}-c_{i}\) 级别。

然后就在 \(O(n+m+k)\) 的时间内解决了,所以 \(k\) 可以做到 \(10^7\) 的。

代码

ICPC 合肥

D. Balanced Array

首先需要注意到:我们存在 \(O(1)\) 判断一个长度为 \(n\) 的序列在某个 \(k\) 下是否合法的方法,直接使用字符串哈希,令 \(base\) 是一个大于 \(2a\) 的数:

  • \(h[1,n-2k]+h[2k+1,n]=2h[k+1,n-k]\)

然后,注意到对于一个 \(k\),不考虑 \(2k+1\le l\) 的限制的话,那么合法的 \(l\) 是一个前缀。加上 \(2k+1\le l\) 的话就是一段区间,所以可以直接对每个 \(k\) 先二分算出这个区间,然后跑区间覆盖,这样是 \(O(n\log n)\) 的。

考虑换一个角度:从小到大枚举 \(k\),然后我们假设之前已经让 \(p\) 这个前缀平衡了,且没有成功让 \(p+1\) 这个前缀平衡。对于此时的 \(k\),如果 \(p+1\lt 2k+1\) 了,那么 \(p+1\) 就永远没有机会平衡了,所以我们直接令 \(p\) 来到 \(2k\)。然后我们不断检查 \(p+1\)\(k\) 下是否是平衡的,如果是,我们就继续向后尝试,否则根据单调性我们直接停在这里,考虑更大的 \(k\) 即可。这样的话就在 \(O(n)\) 的时间内解决了这个问题。

代码

K.Campus Partition

首先注意到:对于一个连通块,我们设最大点为 \(x\),次大点为 \(y\),则只需要保留 \(x\sim y\) 路径上的所有点。

因此连通块划分变成了路径划分。然后一条路径的权值(非退化)应该就是路径端点里边权较小的那个。

然后考虑设计树形 dp:令 \(f(u)\)\(u\) 子树内的答案,\(g(u,x)\)\(u\) 子树内的答案,但是 \(x\rightarrow u\) 被占据,且这条路径只确定了一端点是 \(x\),另一端点在 \(u\) 子树外,还未确定,此时的一个答案。

转移比较显然:当我们加入一个儿子 \(v\) 的时候,首先原始的 \(f(u)\) 会加上 \(f(v)\),然后我们用 \(g(u,x)+g(v,y)+\min\{a_x,a_y\}\) 来更新此时的 \(f(v)\),这是 \(f\) 的变化;至于 \(g(u)\) 就应该是,原本的所有 \(g(u,x)\) 都会加上 \(f(v)\),而 \(g(v,x)\) 会加上之前的 \(\sum f(v)\),然后更新给 \(g(u,x)\)

这个过程显然可以用线段树合并维护,然后就做到了 \(O(n\log n)\)

代码

L. Information Spread

考虑根据题意建出 dfs 树,则一条可能的传播路径一定是沿着树边走的,只有最后一条边可以是非树边。

证明是这样的:如果我们走了一条非树边,那么说明 \(label_u\gt label_v\),其中 \(label\) 是一个点什么时候 \(vis=1\)。那么你就不可能从 \(v\) 往后走了,因为它已经传递过一次自身信息了。

这样的话,对于一个点 \(v\),考虑所有非树边 \(u\rightarrow v(pr=w)\),所有这样的 \((v,w)\),再加上 \((u,1)\),相当于是这样一个事件:

  • \((x,y)\) 表示:如果 \(1\rightarrow x\) 的路径上每条边都生效,那么有 \(y\) 的概率让 \(v\) 被传递到信息。

显然因为有多个事件,所以考虑容斥:计算每一个事件都失效的概率。

那么这就是一个比较套路的问题了:直接建出关于 \(x\) 的虚树,然后 \(dp(u,0/1)\) 表示计算完 \(u\) 子树内的情况,且我们是否强制要求 \(1\rightarrow u\) 的路径上至少有一条边失效,这样的概率。

然后本题在 \(O((n+m)\log n)\) 的时间内解决。

代码

The 1st Universal Cup. Stage 18: Shenzhen

M. Canvas

很好的构造题。但是有点难写。

对于 \(1-1\)\(2-2\) 两种类型的操作,前者我们可以全放最前面,后者我们可以全放最后面。

当我们把一下 \(2-2\) 放在最后面的时候,对于剩下的 \(1-2\) 操作,如果它的 \(1\) 会在后面被染成 \(2\),那么我们也能放心地做这个 \(1-2\) 操作(应该在所有 \(2-2\) 之前,其余 \(1-2\) 之后),然后可以把这个操作也视为 \(2-2\).... 重复这个过程以后,可以化简成一个只有 \(1-2\) 类型操作的问题。

考虑从 \(2\) 的那个点连有向边到 \(1\) 的那个点,然后问题变成我们需要按顺序考虑激活每条边,使得有尽可能多的店,和它相关的边最后一次被激活的时候,是它的出边。

如果这张图是个 DAG,那么很明显答案就是 \(n\) 减去无出度点数 \(x\):显然这是上界,也很容易构造:我们随便跑出一组拓扑序,然后对于所有有出度点 \(u\),随意选择一条 \(u\rightarrow v\) ,最后按照 \(u\) 的拓扑序,从前往后枚举每个 \(u\),点亮这条 \(u\rightarrow v\)。除去这 \(n-x\) 条关键边,剩下的边就直接全放最前面即可。

现在考虑这张图是一个 scc 的情况:显然,不可能所有点最后都合法,因为让一个点变成 \(2\) 总意味着一个点会变成 \(1\):所以答案上界是 \(n-1\),并且很容易发现是一定能取到的:我们随意选择一个点,那么它一定能被所有点走到,因此在反图上 dfs 建出 dfs 树的一个树形结构,然后从叶子开始激活向父亲的边,这样就只会有根节点最后不合法。

结合上面的两个构造,很容易导出一般情况的结论:答案就是 \(n\) 减去缩点后的无出度点数 \(x\):这个值是答案的上界同样容易证明。并且容易给出构造:对于所有 scc,若其有出度,则我们令关键点为某个有连向 scc 外的连边的点,否则随意钦定其中一个点为关键点。然后对于每个 scc,以关键点为根,跑出内部 dfs 树的树形结构。对于每个有出度的 scc 的关键点,随意连向一个另外的 scc。显然此时这些保留的连边形成一个 DAG,按照拓扑序的顺序激活即可。

此时我们就在 \(O(n+m)\) 的时间内解决了这个问题。

代码

ICPC 杭州(2022)

加训随到的,感觉题的质量有点两极分化了,难的太难,而且数量还不少,简单的题又太傻逼。

E. Oscar is All You Need

牛魔题我草。和 JS 的省队人数一样魔怔。

首先打表发现 \(n\ge 4\) 的时候一定能做到排序,而 \(n=3\) 的情况是平凡的。所以我们就考虑用 \(\le 2n+1\) 次操作排序。

那么一个很自然的想法是考虑按顺序考虑每个 \(x\),然后把 \(x\) 放进第 \(x\) 个位置。

\(1\) 是容易的:如果 \(rk_1\ge 3\),那么我们可以 \((1,n-rk_1)\) 一次操作带走;如果 \(rk_1=2\),那么我们考虑 \((2,1)\)\((1,1)\) 两次操作也带走了。

接下来考虑假设 \([1,x]\) 已经归位,我们把 \(x+1\) 归位。但这样会发现一个问题:我们只能做到在 \(rk_{x+1}\neq x+2\) 的时候使用两步归位(这里可以自己手玩一下,玩不出来也没事,反正不是正解),但是 \(rk_{x+1}=x+2\) 的时候就做不到了。

这个时候一个很容易想到的例子就是 \(1,2,4,3\):那么打表研究一下 \(1,2,4,3\) 的排序方法:会发现有且仅有 \(5\) 步的解:并且按照它的排序方式来做,确实能得到一个,在 \(x\gt 1\) 的情况下,把 \(x+1\) 这个数,从 \(x+2\) 扔进 \(x+1\) 的方法。看上去全对了啊?全错了!因为步数会超。

然后这个方法就走死了。得换个排序方法。

考虑这样一种插入排序方式:我们不断找到前缀的极长升序段长度 \(p\),当 \(p\lt n\) 的时候,把最后一个元素插入到 \([1,p+1]\) 中的对应位置。 而且把最后一个元素插进去是很方便的:假设要插进第 \(x\) 个元素之后,那么我们 \((x,2)\)\((1,x)\) 两次操作就够了。

这个过程里有两个隐含条件:\(x\gt 0\)\(x\lt n-2\)

对于 \(x\gt 0\),是很容易保证的:我们最开始依旧可以花费 \(2\) 次操作,来让 \(1\) 位于开头。

对于 \(x\lt n-2\),当 \(x=n-2\) 的时候,我们曾经得到的那个 \(5\) 步的构造就能排上用场了。

这个时候次数就对了,因为不会出现最开始那样很多次都需要用 \(5\) 步的情况了。

总结一下就是很需要洞察力的题,也需要暴力辅助,不然很难观察到 \(5\) 步的一个搜索方法。

代码

H. RPG Pro League

djq 在 WC2023 的一道例题,当时没怎么听,但好像现在给我整明白了!

首先考虑怎么让答案最大:考虑建出网络流模型,我们分成四个类:\(D/S,D,S,B\)。然后一个组就相当于每个类都恰好有一个人。此时我们建出二分图结构:左边是 \(4\) 个点代表 \(4\) 个类别,右边是 \(n\) 个点代表 \(n\) 个人。若 \(x\) 这个人能被放进第 \(y\) 类,就连 \(L(y)\rightarrow R(x)\) 容量为 \(1\)。然后 \(x\rightarrow n\) 的容量也为 \(1\)

我们二分答案的话,那么 \(s\) 到每个左部点的容量就是恰好是 \(ans\),然后看最大流能不能跑满就行。

那么启动模拟最大流找增广路(为什么不考虑转求最小割?因为等会总归还要模拟费用流):

考虑如果你从左部点 \(x\) 走到右部点 \(u\),再走到左部点 \(y\)(这条应该是反向边)。那就是本来连的 \(y\rightarrow u\),现在改成了 \(x\rightarrow u\)

那你注意到我们并不关注 \(u\) 是谁,我们只关注是否存在一个点 \(u\),使得:\(y\)\(u\) 原本匹配,且 \(x\)\(u\) 也可以匹配。这就等价于我们可以从 \(y\) 走到 \(x\)

那么一条增广路就是从一个有空余容量的左部点出发,然后走若干步,到另一个左部点, 然后去到一个右部点。

因为左部只有 \(4\) 个点,所以可以暴力计算任意两点的可达性,然后直接暴力找到一对左部点的点对 \((u,v)\),然后 \(s\rightarrow u\rightarrow ... \rightarrow v\rightarrow p\rightarrow t\),其中 \(p\) 就是一个空着的右部点。

这样我们就完成了这个最大流的模拟:那注意到一个事情是我们不需要二分答案了,我们直接从小到大枚举答案,然后对每个左部点再尝试暴力增广一下就好了。

现在我们求出了最大流,我们考虑带费用的情况:那就是每个右部点到 \(t\) 的连边带费用。显然最大流求出的就是一组 \(p_i=0\) 的 MCMF,当我们修改费用的时候,我们就不需要考虑增广路,只需要消去负权圈即可。

那么我们修改 \(x\rightarrow t\) 的边权的时候,显然只会有一次可能的消圈,且这个圈包含 \(x\rightarrow t\) 这条边(也可能是其反边)。

讨论一下 \(x\rightarrow t\) 本来是否有流量,两种情况是类似的。下面仅讨论 \(x\rightarrow t\) 原本无流量的情况。

那一定是有两个左部点 \((u,v)\),然后撤销掉一个和 \(u\) 匹配的点,然后从 \(u\) 走到 \(v\),然后 \(v\)\(x\) 匹配上。

依旧暴力计算左部点的任意两点可达性,然后暴力枚举 \((u,v)\) 即可。显然我们并不关注 \(u\sim v\) 路径上的那些人,只关注 \(u\) 那里撤销掉匹配的点,这个点的 \(p\) 应该尽可能大。所以拿 set 维护就好了。

然后就在 \(O((n+m)\log)\) 的时间内解决了这个问题。

代码

ARC169

哎呦 arc 又被我们凯文大神杀爆了,何训?

D. Add to Make a Permutation

非常好的题目!

我们先不考虑模意义下这件事情:令 \(b_i\)\(a_i\) 最后会变成的数。则令 \(M = \sum_{i}b_i-a_i\)。显然 \(M\) 需要是 \(m\) 的倍数,同时 \(\forall i,b_i\ge a_i\),这两点比较平凡。注意到我们实际上是在 \((b_i-a_i)\) 这个序列里,每次选 \(m\) 个不同的正数 \(-1\)。这是一个很经典的问题,研究最多能进行的操作次数是比较复杂的,必须 \(O(m)\) 计算;但是判断能否消尽,就仅需要在上述平凡条件以外加上:\(\max (b_i-a_i)\le \frac{M}{m}\)。也不难归纳证明。

\(b_i\) 需要在模 \(n\) 意义下构成 \(0\sim n-1\) 的排列:不难猜测 \(b_i\) 一定是连续的 \(n\) 个数,也很容易证明:假如有两个数的差值 \(x-y\gt n\),则我们替换成 \(x-n\)\(y+n\),在不改变 \(M\) 下,极差一定不会变小。这样就证明了我们的结论。不妨令 \(b_i\) 形如 \(x,x+1,x+2,x+3,...,x+n-1\)

然后也很容易感知到:把 \(a\) 排序后 \(b\) 应该也对应升序,证明同样考虑反证。假如 \(a_i\lt a_j\)\(b_i\gt b_j\),则交换,显然不违背 \(a_i\le b_i\) 的条件,极差也会更小。

那么结合上面两点,就应该是 \(a_i\)\(x+i-1\) 匹配。

到这里就是一个很可做的问题了:我们只需寻找最小的合法的 \(x\) 即可。

\(m\mid M\) 非常容易满足:令 \(b=\sum_{i=1}^{n}i-1-a_i\),那么注意到 \(M=nx+b\),因此就有 \(nx\equiv -b\pmod{m}\)。先暴力找到最小的 \(x_0\) 满足 \(nx_0\equiv -b\pmod{m}\),然后显然任何 \(x\) 都能表述成 \(x_0 + k\frac{m}{\gcd(n,m)}\)。令 \(p:=\frac{m}{\gcd(n,m)}\)。也就是 \(x=x_0+kp\)

\(b_i\ge a_i\) 的限制,也就是给 \(k\) 钦定了下界,容易算出。

事实上 \(b_i-a_i\le \frac{M}{m}\) 也给出了 \(k\) 的下界,是这样分析的:我们当 \(x\) 不断每次增加 \(p\) 的时候,显然 \(\frac{M}{m}\) 每次增加 \(\frac{np}{m}=\frac{n}{\gcd(n,m)}\)。而注意到 \(n\gt m\),所以 \(\frac{M}{m}\) 永远增长得要更快,所以确实存在一个单调性。这里为了方便,可以直接二分算出下界。

然后我们就在 \(O(n\log n)\) 的时间内解决了这个问题。

代码

NERC2023

H. Hypercatapult Commute

很清新的构造题。

首先,我们考虑两个图 \(G_1,G_2\)。如果有一个人要从 \(a\) 连向 \(b\),则在 \(G_1\) 中连一条 \(a\rightarrow b\) 的边;如果最后我们有一次传送操作,从 \(x\) 传送到 \(y\),则在 \(G_2\) 中连一条 \(x\rightarrow y\) 的边。

显然,\(G_2\) 的每个弱联通分量都应该是一个树/基环树的形式,且不难发现 \(G_1\)\(G_2\) 的弱联通分量应该是逐个对应的。所以我们不妨考虑对 \(G_1\) 的每个弱联通分量,算出它的最优的 \(G_2\),拼接在一起即可。接下来我们假设 \(G_2\) 就是一个弱联通分量。

如果 \(G_2\) 是一颗内向树的形式,则比较显然地,我们从叶子开始按顺序执行传送会比较优秀,\(G_1\) 中的边只要是从后代连向祖先,就都可以满足要求;如果 \(G_2\) 是一颗内向基环树的形式,不难发现最后执行的一条边一定是在环上的,我们不妨先断掉这条边 \(x\rightarrow y\),然后剩下的边应该构成一颗以 \(x\) 为根的内向树。

什么时候 \(G_2\) 为内向树就足够了?显然当且仅当 \(G_1\) 构成一个 DAG,我们按照拓扑序传送即可。否则我们考虑暴力枚举最后一次传送操作 \(x\rightarrow y\),则:

  • 我们暂时删除 \(G_1\) 中所有终点为 \(y\) 的边,因为这些边可以从 \(u\rightarrow y\) 调整为 \(u\rightarrow x\)
  • 此时的 \(G_1\) 应该是一张 DAG,且因为我们最后要做一次 \(x\rightarrow y\) 的传送,所以 \(x\) 不能有出度。

当删除完所有终点为 \(y\) 边的时候,若 \(G_1\) 是 DAG,我们再尝试加入这些边:也就是有一些点 \(w\neq x,y\),它们需要决策连向 \(x/y\),使得最后的图依然是 DAG。

如果只有一条边需要加入,我们只需要看原本的 \(G_1\) 里,\(x\rightarrow u/y\rightarrow u\) 的可达性,而注意到 \(x\) 原本是没有出边,所以总能把这条边塞入 \(x\)。于是我们直接无脑把所有原先连向 \(y\) 的人重新连向 \(x\),然后检查这张图是否是 DAG 即可。

时间复杂度 \(O(nm)\)

代码

THUPC 2024 初赛

有一说一,这一场的题目质量我认为是不错的,但是在赛时组织上也确实比不上正规的 ICPC?

B. 一棵树

似乎还有对每个 \(k\) 求答案的版本,到时候学习一下看看实力。

考虑直接设 \(dp(u,x)\)\(u\) 子树内选择了 \(x\) 个点的答案:我们猜测其是凸的并且归纳证明之:假设 \(dp(v)\) 全部为凸的,我们直接闵和卷起来就可以,因此还是凸的;最后我们考虑 \(u\rightarrow fa\) 的这条边,假设 \(u\) 子树内有 \(x\) 个点那么答案就会加上 \(|2x-k|\),显然这样是一个关于 \(x\) 的下凸函数。因此每一个 \(dp(u,x)\) 都是关于 \(x\) 的下凸函数。

考虑如何维护?首先由于有闵和在,所以考虑维护差分,这样的话闵和就等价于归并差分。

然后加上 \(|2x-k|\) 的话,那么讨论一下 \(k\) 的奇偶性,大概是要么斜率为 \(-2/2\),要么中间还多一个 \(0\)。我们可以维护对顶的差分:也就是按照 \(\lfloor \frac{k}{2} \rfloor\) 为分界点,这样的话两边,就是整体加一个数了(对于中间多 \(0\) 的情况,可以先把最左边的拿出来,然后剩下的加 \(2\),然后再将其插入回去)。

由于有差分归并的缘故,我们使用可并堆维护会非常方便,这里使用左偏树,可以很好地支持打加法标记。时间复杂度 \(O(n\log n)\)

代码

G. 采矿

很牛的 dp 题,自己想出来+修完过掉感觉还是很有成就感。

我们称子树补 \(x\) 是挖掉子树 \(x\) 的结果。

考虑如果机器人永远位于 \(s\) 不动,那么我们就只关注子树补 \(s\) 内的人数:假设有 \(x\) 个人,每一轮这 \(x\) 个人都应该能任意排布。所以我们只需要预处理出每个子树补,选若干个点站人,能获得的最大收益。

当机器人上行 or 下行的时候,就会出现移动完之后,子树内还有人的情况。

容易想到 \(f(i,s,x)\) 表示第 \(i\) 次计划执行完后,机器人位于 \(s\),且子树内选择了 \(x\) 个点站工人的最优代价。但是这个 dp 是有问题的:比如说如果我们下行,那么你发现我们可以决定向左拐还是向右拐,然后如果向左拐最后拐到 \(s'\),那么新的 \(x'\) 应该不超过原本 \(s\) 的左子树内部的结点数。(考虑我们可以把一些工人提前移动到 \(s\rightarrow s'\) 这条链向外延伸的那些部分,然后再把机器人从 \(s\) 移动到 \(s'\))。所以我们应该需要分别记录左右两颗子树的工人数量(注意到此时子树补的工人数量也随之确定)。也就是 \(f(i,s,x,y)\)。注意到状态数是 \(\sum_{u}sz_{lc_u}\times sz_{rc_u}\),根据树形背包的分析,有效状态总数还是 \(qn^2\) 的,只需要滚动数组即可。

接下来考虑转移。首先我们预处理出每个子树和每个子树补选若干个工人的最优方案,然后:如果机器人没有移动,那么是容易的,因为等价于子树补这部分添加/删去一个人;如果机器人是下行的,则我们先讨论进入的是左子树还是右子树,这决定了我们能自由分配的人数是 \(x\) 还是 \(y\)。首先我们知道 \(lc(s)\) 这部分有 \(x\) 个人,然后我们如果还要继续下行,那么假设我们继续往左子树递归,就可以分出 \(x\) 个人中的若干个给右子树,剩下的再递归下去分配,向右递归同理。那么更具体的,做法就是:设状态 \(F(u,x)\),初始的时候,\(f(i,s,x,y)\rightarrow F(lc(s),x)\) 以及 \(f(i,s,x,y)\rightarrow F(rc(x),y)\)。然后对于 \(u\) 的两个儿子 \(p,q\)\(F(u,x+y)+g(q,y)\rightarrow F(v,x)\),这里的 \(g(q,y)\) 是预处理出来的 \(q\) 子树内选 \(y\) 个工人的方案数(对于 \(p,q\) 互换也做一次)。然后 \(dp(i+1,s,x,y)\) 就可以利用 \(F(s,x+y)\) 求出。

对于机器人上行也有类似的 dp,然后整个问题在 \(O(n^3+qn^2)\) 的时间内解决。

代码

AGC065

AGC的题真是做不了一点,完全没脑子唉唉。

A. Shuffle and mod K

怎么 A 题都不会?

首先考虑:我们可以把 \((a_i-a_{i-1})\bmod k\),改写成 \(a_{i}-a_{i-1}+k\times [a_i\lt a_{i-1}]\)

这样的一个好处是:我们可以把最大化的东西看成是:\(\sum_{i=2}^{n}a_{i}-a_{i-1}+k\times [a_i\lt a_{i-1}]\),也就是 \(a_{n}-a_{1}+k\times \sum_{i=2}^{n}[a_i\lt a_{i-1}]\)

\(x:=\sum_{i=2}^{n}[a_i\lt a_{i-1}]\)。我们考虑最大化 \(x\)。容易发现,假设最优解取到的是 \(x'\),那么一定有 \(x'\ge x-1\)

注意到有 \(x\)\(a_i\gt a_{i+1}\) 等价于序列能被划分成 \(n-x\) 个下降链,所以不妨从这个角度来考虑。

\(x\) 的最大值能取到多少?如果序列互不相同,我们直接把他们取出来降序排列即可;如果序列有相同,我们就不断重复这个过程。注意到假设序列中出现次数最多的元素出现了 \(p\) 次,我们就得到了 \(p\) 个下降序列,也就让 \(x\) 取到了 \(n-p\),显然这是 \(x\) 的一个上界。

现在来考虑,固定 \(x'=x/x-1\) 的时候,\(a_n-a_1\) 的最大值是多少:

  • \(x'=x\)。注意到,我们考虑所有出现次数 \(=p\) 的元素:\(b_1\lt b_2\lt ... \lt b_m\)\(p\) 条下降链中这些元素都会出现。\(a_n-a_1\) 的实际意义是:最后一条链的尾部(最小值),减去第一条链的头部(最大值)。那么注意到,任何一条链的 \(\max\)\(\ge b_m\),任何一条链的 \(\min\)\(\le b_1\)。所以一个上界是 \(b_1-b_m\)。很明显这个上界是可以达到的:首先我们排出 \(p\) 条下降链 \(b_m\rightarrow b_{m-1}\rightarrow ... \rightarrow b_1\),然后再依次插入所有出现次数 \(\lt p\) 的元素:它总能插进序列非头尾的一个位置,且不改变下降链条数。

  • \(x'=x-1\)。那么我们就凭空多出了一条链:我们不妨考虑这条新链上所有元素的构成。首先我们至少要有一个出现次数 \(=p\) 的元素不被选中,否则剩下的元素最多只能拼出 \(p-1\) 条链。然后我们考虑,由于你只有一条新链,所以第一条链 / 最后一条链,一定有至少一条是旧链。两个都是旧链的情况就是 \(b_1-b_m\),所以一定是其中一条是新链,另外一条是旧链。不妨设:最后一条链是旧链,第一条链是新链。然后我们来考虑,最小的没有被选中的,出现次数 \(=p\) 的元素 \(b_y\)。显然因为任何旧链都包含 \(b_y\),且一定存在一条旧链的最小值就是 \(b_y\),因此就有 \(a_n=b_y\)。然后我们只需最小化 \(a_1\) 的值,也就是让新链选出去的人的最大值尽可能小。显然 \(b_1\sim b_{y-1}\) 是必须选出去的,那么我们也只用选这些位置。你发现这样 \(a_n-a_1\) 就等于 \(b_y-b_{y-1}\);然后讨论一下第一条链是旧链,最后一条链是新链,其实也是一模一样的结论。因此我们得到,\(\max a_n-a_1=\max_{y=2}^{m}b_y-b_{y-1}\)

然后直接比较两部分的优劣,即可在 \(O(n\log n)\) 的时间内解决这个问题,如果值域够小也可以做到 \(O(n+k)\)

代码

B. Erase and Insert

怎么 B 也会不了一点?完全不明白正解是如何想到的啊。

可能还是能想到的:假设我们已经重新插入了 \(1,2,...,m\),称这些数为基准数。那么我们考虑此时序列的形态:可以看成是,\(1\sim m\) 在排列中的相对顺序,中间和两边插入了一些数,并且我们知道从左往右,这些数就是 \(m+1\sim n\)

\(x_0,x_1,...,x_m\) 是相邻两个基准数之间,插入了多少个数。则我们取出 \(m+1\) 再插入可以看成是:

  • 找到最小的 \(x_i\gt 0\),然后将其减一(对应取出)。
  • \(y\) 是有多少个 \(\le m\) 的元素,在 \(p\) 的出现位置先于 \(m+1\)。那么我们会把 \(m+1\) 插入第 \(y\) 个空,因此,我们可以任意把 \(x_y\) 拆分成两个和固定的非负整数。

然后我们要从 \((n)\) 变为 \((0,0,...,0)\)(共 \(n+1\) 个)。但是拆分是难以考虑方案数的,我们倒过来考虑合并!

\((0,0,...,0)\) 开始,变成 \(n\),然后:

  • 合并 \(x_y\)\(x_{y+1}\)
  • \(p\) 是序列 \(x\) 极长的 \(=0\) 的前缀,然后我们可以选择 \([1,p+1]\) 中的一个位置 \(+1\)。这个过程结束后 \(p\) 就会改变。

\(dp(i,j)\) 是我们考虑完了 \(i\sim m\),然后我们可以选择 \([1,j]\) 中的位置 \(+1\)(也就是 \(p=j-1\))的方案数。然后转移的时候我们讨论 \(y\)\(j\) 的大小关系即可。朴素实现需要枚举 \(+1\) 的位置,但是可以前缀和优化做到 \(O(n^2)\)

代码

SWERC 2023

A. AND-OR closure

这个题看上去毫无思路,我们需要更具体地刻画一个数能被表示出来的充要条件。

\(S\) 是所有至少在一个 \(a_i\) 中为 \(1\) 的 bit 的集合。然后我们选中其中的一个位置 \(x\),将所有 \(a_{i,x}=1\)\(a_i\) 全部 and 起来。此时得到的数 \(A\) 满足:

  • 如果 \(A_y=1\),则等价于,\(\forall a_{i,x}=1,a_{i,y}=1\) 亦成立。

反过来:我们发现 and/or 操作也一定不改变这个性质:就是说如果有这样的一对 \((x,y)\),那么你不管怎么进行操作,只要 \(A_x=1\),就一定有 \(A_y=1\)

结合上面两点。我们对所有 \(x,y\) 连边 \(x\rightarrow y\),然后:一个 \(A\neq 0\) 能被表示出来,当且仅当 \(A_x=1\),则所有 \(x\) 能走到的位置 \(y\) 都满足 \(A_y=1\)。而 \(A=0\) 能被表示出来,显然等价于 \(a_1\and a_2\and ... \and a_n=0\)

若有 \(x\rightarrow y\),意味着 \(cnt_x\le cnt_y\)。这是一个弱的偏序关系:但注意到若 \(cnt_x=cnt_y\)\(x\rightarrow y\) 存在,则一定也有 \(y\rightarrow x\),换言之这两个 bit 是等价的:一定同时为 \(0/1\)。所以我们可以合并这些 bit,令新的 bit 集合为 \(S'\)。然后剩下的 bit 的连边关系应该按照 \(cnt_x\) 为拓扑序构成 DAG。

此时我们要计算的就是 \(S'\) 的子集数目:满足若一个点在子集内,则它连边连到的所有点也在子集内。

此时 \(a_i\le 10^{12}\) 终于排上用场:我们直接折半 + 高维前缀和优化即可。这部分是平凡的。

代码

I. Impossible Numbers

好题!

首先来考虑:如果给定一个数,如何判断其是否能被表出?

显然我们只关注数字构成的多重集合。一个比较套路的事情是这种问题基本只能建出二分图匹配模型后利用 Hall 定理解决。也就是左部 \(d=10\) 个点的二分图,右部 \(n\) 个点代表骰子。

那么我们 \(2^d\) 枚举左边的一个集合 \(S\),令 \(f(S)\)\(S\) 集合内的数字在我们想表出的数里出现的总次数;而 \(g(S)\) 是有多少个筛子,至少有一面是 \(S\) 内的数。则不合法等价于 \(\exist S,f(S)\gt g(S)\)。注意到 \(g(S)\) 是确定的,而 \(f(S)\) 是根据询问数的不同而不定的。

一个困难的点在于:我们依然无法快速地刻画一个具体的数是否能被表出。考虑这样一个事情:我们令 \(v(S)\) 是多重集合 \(S\) 能表示出来的最小数(显然是开头放最小的非 \(0\) 数字,剩余的升序排列置于其后),则求出 \(v(S)\) 最小的前 \(k\)\(S\):然后注意到我们可以让一个 \(S\) 变换排列顺序,而显然答案应该是 \(k\)\(S\) 能生成的数,归并后的结果的前 \(k\) 项。

如果求出了前 \(k\) 优的 \(S\),就可以用堆来找到前 \(k\) 小的答案,这是经典的堆贪心套路。现在问题就是如何求出这些 \(S\)。先来明确 \(S\) 之间的比较:

  • 若大小不同,则认为较小的那个更优。
  • 否则,先比较两者的最小非零元;若依旧相同,再把剩余元素升序排序,比较字典序。

因此,先枚举大小和第一位,此时再用搜索的方法逐个求出前 \(k\) 优解:我们直接挨个确定 \(0\sim 9\) 的出现次数(不含第一位),优先让当前考虑的数字出现次数尽可能多即可。这是搜索的大体思路。

具体实现时:我们需要保证当前总能转移到一个可能的 \(S\):也就是不能出现搜到底都搜不出来解的情况。显然加上这个剪枝,进入搜索函数的次数就是不超过 \(dk\) 的。为了快速判断是否继续搜能有解:这等价于是否存在一个 \(f(S)\gt g(S)\),我们考虑枚举数字 \(x\) 的出现次数的时候,记录是否存在这样一个 \([0,x)\) 的子集 \(S\) 符合要求:

  • 若存在,则 \(x\) 出现多少次都允许。
  • 若不存在,则分两种可能:要么是有一个 \([0,x]\) 且包含 \(x\) 的子集 \(A\) 需要满足要求:我们枚举这样的 \(A\),则作出了对 \(x\) 的下界要求;要么是有一个最大元素在 \((x,9]\) 之间的子集 \(B\) 需要满足要求:此时不难证明,如果存在这样的 \(B\),则一定至少有一个 \(B'\),使得仅有最大元素出现在了 \((x,9]\) 范围。我们枚举这个最大元素 \(d\)\(B'-d\) 的结果:然后给出了对 \(x\) 的上界要求。

因此最后可以求出两个界 \(l,r\),使得:

  • \(cnt_x\ge r\) 则向后搜有解(且后面可以任意选了)。
  • \(cnt_x\le l\) 则向后搜有解。

然后直接根据解出的约束,从大到小枚举可行的 \(cnt_x\) ,然后递归搜索即可。

我们来分析复杂度:可以看到对于任意一个 \(S\),确定 \(cnt_x\) 的时候花费的代价是 \(2^{x}\times (d-x)\)

\(\sum_{x=0}^{d}2^x\times (d-x)\) 显然是不超过 \(2^d\) 的。因此对于任意一个 \(S\),它的花费不超过 \(2^d\)。(这里因为不同的 \(S\) 在搜索树上肯定还有重合部分,所以实际花费会更小)。

这样搜索的部分就是 \(O(2^d\times k)\),而后半部分堆贪心则是 \(O(nk\log)\),两部分常数都极小。

代码

Codeforces Pinely Round 3

F2. Small Permutation Problem (Hard Version)

感觉这个题其实不止 2500 啊。

先考虑没有 \(-1\) 怎么做?一个显然的要求是 \(a_{i-1}\le a_i\le a_{i-1}+2\)\(a_n=n\)

我们可以来对三种情况分类讨论:

  • \(a_i=a_{i-1}\),则 \(p_i\) 必须 \(\gt i\),称这些位置 \(i\) 为特殊位置。
  • \(a_i=a_{i-1}+1\),则 \(p_i\) 必须 \(\le i\)
  • \(a_i=a_{i-1}+2\),则一定是:\(p_i\lt i\) 且前面必须有一个特殊位置是等于 \(i\) 的。

第三条限制启发我们:顺序考虑每个 \(p_i\) 的取值,扫到特殊位置的时候,先不考虑其具体值。然后记录变量 \(r\),表示 \(\le i\)\(p\) 里还没有确定取值的特殊位置数量。这样的话,进入第三种情况时,我们在 \(r\) 个位置里任意选择一个位置填上 \(i\) 即可。并且我们知道,\(\lt i\) 的还没有填的数也恰好有 \(r\) 个,所以 \(p_i\) 的取值数目也容易确定。

这样容易在 \(O(n)\) 时间内解决 F1。我们来考虑 F2。

一个容易走错的方向是:依旧逐个考虑 \(a_i\),然后讨论 \(-1\) 的取值是 \(a_{i-1}/a_{i-1}+1/a_{i-1}+2\)。这样非常难以处理。我们需要更换思考方式。

首先我们强制令 \(a_n=n\),然后假设我们考虑完了 \(a_1\sim a_i\):直接找到 \(\gt i\) 的第一个 \(a_j\neq -1\),然后 \((i,j)\) 范围内全是 \(a_i=-1\),令 \(x=j-i-1\),也就是 \(-1\) 的个数。

考虑直接一鼓作气,确定 \(p(i,j]\) 的取值:假设 \(y=a_j-a_{i}\),我们依旧令 \(r\)\(1\sim i\) 里有几个位置没有填,则我们发现如下约束:

  • \(\le i\)\(r\) 个位置,如果此时要填数,则只能填 \((i,j]\) 之间的数。

  • \((i,j]\) 这些位置,可以任意填 \(\le i\) 的数。

  • 我们要求这一轮一共新填 \(y\) 个数。

考虑直接枚举 \(0\le c\le y\),表示我们本轮在 \(\le i\)\(r\) 个位置里选出了 \(c\) 个位置填数。则接下来的方案数容易 \(O(1)\) 计算。

注意到 \(c\) 有下界 \(y-x+1\),所以一次的枚举量是 \(O(x)\) 的,总枚举量就是 \(O(n)\) 的。

这样我们就在 \(O(n)\) 的时间内解决了问题。

代码

G. Pumping Lemma

很酷的题目!

数据范围看上来应当是 \(O(n)\) 或者 \(O(n\log\log n)\) 之类,猜测一定需要到一些关键性质。

\(n=m\) 的情况是平凡的,先特判。然后假设 \(n\lt m\)

\(S\) 形如 \(abc\)\(T\) 形如 \(abbbc\)

假如我们已经确定了一组 \((a,b,c)\),好像我们还是无法看出两个串有什么性质。考虑通过一组解拓展到另一组解。

我们如果把 \(a\) 的长度增加 \(1\) 而不改变 \(b\) 的长度,则注意到此时 \((a',b',c')\) 依然合法,当且仅当 \(c_1=b_1\)

那发现这只和 \(x=lcp(S,T)\) 有关:也就是上述事件合法当且仅当 \(|a+b|\lt x\)。你注意到,\((a,b,c)\) 合法的一个必要条件是 \(|a+b|\le x\)。所以这两者是非常相似的。

还有说法!我们把 \(S,T\) 右对齐,那还可以一样的手法做一波,也就是这次考虑增加 \(c\) 的长度:那令 \(y=lcs(S,T)\),合法当且仅当 \(|b+c|\lt x\),而 \((a,b,c)\) 合法的另一个必要条件是 \(|b+c|\le x\)

到这里已经很明朗了:\(|a+b|\le x\)\(|b+c|\le x\) ,钦定了当 \(|b|\) 固定的时候 \(|a|\) 的取值范围。而在这个取值范围内可以说明:只要一个 \((a,b,c)\) 合法,则其余的 \((a,b,c)\) 都一定合法。

因此我们只需要枚举 \(|b|\) 然后判断区间内任意一对 \((a,b,c)\) 。如何判定?使用哈希即可。

一个细节是上述讨论基于 \(b\) 非空,但 \(b\) 为空是容易特判的。

时间复杂度 \(O(n)\)

代码

杂题

ARC112F Die Siedler

神题!

首先注意到,如果没有卡包,我们的策略可以是从前往后考虑(考虑完 \(n\) 就回来考虑 \(1\)),然后看是否有 \(2i\) 张卡牌 \(i\),有的话就升级。还注意到一个事情是,我们不需要严格按这个 \(1\sim n\) 的顺序考虑也是对的:也就是我们不断地任意选择一个 \(2i\)\(i\) 然后升级,直到无法进行任何操作为止,这也是不影响答案的。

这启发我们初始把一张 \(i\) 号牌替换成 \((2i-2)!!\)\(1\) 号牌,然后整个牌堆就只有 \(1\) 号牌了。还有一点是,我们可以从 \(n\) 换到 \(1\),所以我们可以把 \((2n)!!\)\(1\) 号牌换成一张。那么令 \(S=(2n)!!-1\) 我们的这个 \(1\) 号牌数就可以认为是在 \([1,S]\) 意义下(注意不是 \(\bmod S\) 意义下)的东西。

然后令卡包的数是 \(b_i\),那么我们令初始的数是 \(a\),能得到的所有数就是 \(a\) 加上 \((b,...,S)\) 的一个线性组合,其中 \(b\) 的系数只能是非负数,而 \(S\) 的系数是非正数。根据 bezout 定理,此时我们能加上的就是 \(\gcd(b,...,S)\) 的任意非负倍数。也就是 \(a+kg\)(在 \([1,S]\) 意义下)都能被表出。

那么不妨令 \(a:=a\bmod g\),则我们可以直接枚举 \(k\lt \frac{S}{g}\),然后再暴力计算每个 \(a+kg\) 最后会剩下的卡牌张数。但是当 \(\frac{S}{g}\) 的时候这就会出问题。需要注意到的一个,非常重要的事情是,在 \(n\le 16\) 的情况下,\((2n)!!-1\) 的因子非常有规律:要么极大,要么极小。事实上最接近的一对 \(x\times y=S(x\le y)\)\(x\) 是一个 \(10^6\) 左右的数,所以 \(g\)\(\frac{S}{g}\) 总有一个是在我们可以接受的范围内的,刚才我们已经得到了一个 \(O(\frac{S}{g})\) 的算法,现在我们只需要得到一个 \(O(g)\) 的算法就够了。

此时我们不再从确定 \(a+kg\) 后计算局面的概率考虑,而是从,每次往局面里加入一张任意卡,然后更新 \(a+kg\) 的局面来考虑。也就是我们令 \(dis(x)\) 是所有 \(\equiv x\pmod g\) 的状态的里面,张数最小的。转移的时候,直接枚举 \(1\sim n\) 的一张卡牌加入即可。那么这就是在跑同余最短路,容易做到 \(O(gm)\)

然后两部分拼一下,就解决了本题。

代码

CF865F To Play or not to Play

很趣味的数据结构!

尝试找到一些贪心性质,但是并没有什么好的说法:一个值得注意的事情是我们永远会在两个人同时能上线的时候都选择打游戏(即使他们的差 \(\gt C\)),而不会出现一个人 play 另一个人摆烂的情况。因此我们离散化分段后,这种情况可以视作一个整体考虑。除此之外发现整个过程还是非常复杂的,可能会有很多的情况要考虑到,因此贪心是一个不太有前途的选择。

考虑只有两个量的话很容易想到平面上的点 \((a,b)\) 什么的,然后就考虑直接维护所有点。

这个时候发现有一个性质:就是如果存在 \((a,b)\)\((a',b')\) 满足 \(a\ge a'\) 以及 \(b\ge b'\) 的话,\((a',b')\) 我们就不再关注了。所以我们维护的点,一定是形如 \(a\) 严格递增且同时 \(b\) 严格递减的。正确性基于一点:如果 \(a\gt b\)\((a,b)\) 可达那么 \((a-1,b)\) 一定可达,\(a\lt b\) 的时候就是 \((a,b-1)\) 一定可达。然后不难说明,其实感性理解也很容易。

当加入的是 A only / B only 的时候,那要么就是 \((a,b)\rightarrow (a+x,b)\),或者 \((a,b)\rightarrow (a,b+x)\),是比较平凡的。关键是 both 的时候,我们就需要一些讨论:

  • 如果 \(|a-b|\le C\),显然这样的 \((a,b)\) 在我们维护的点集里,是一段区间 \([l,r]\)。然后 \([1,l)\)\(a\gt b+c\) 的,\((r,m]\)\(a\lt b-c\) 的。那么我们直接变成 \((a+2x,b+2x)\) 即可。
  • 否则以 \([1,l)\) 这部分为例: 我们可以选择 \((a,b)\rightarrow (a+x,b+x)\),也可以选择 \((a,b)\rightarrow (b+c+x,b+x)\);那这部分看上去就要点数 \(\times 2\) 了,但是注意到一点是:后一种转移里我们只要让 \(b\) 最大即可,所以这类转移只会有一个最优的点。换言之就是在 \((a,b)\rightarrow (a+x,b+x)\) 的基础上又插入了一个新点。

最后就设计三部分的合并了:先不考虑插入,然后两侧会有一部分点被中间的干掉,这里可以直接在平衡树上二分。最后再来考虑插入点:那么也是考虑,如果可以加入的话,就在平衡树上二分,干掉前面的一些点即可。

这样时间复杂度即为 \(O((n+m)\log )\)

代码

牛客23 Round8 E

题意:对长度为 \(n\) 的数组计数,满足:

  • \(\forall i,L\le a_i\le R\)
  • \(S=\sum_{i=1}^{n}a_i\),然后令 \(d(S)\)\(S\) 的各数位和,\(d_2(S)\)\(S\) 的各数位平方和,则需要满足 \(d(S)\equiv d_2(S)\pmod{m}\)。(十进制下)

\(n,m\le 20,1\le L\le R\le 10^{1000}\)

很精妙的 dp 题。

首先我们考虑去掉下界 \(L\):我们看成每个数的范围在 \([0,R-L]\) 选。然后有一个额外的加数 \(nL\) 即可。

然后考虑上界 \(R\) 使用容斥去掉:但这样有个问题就是数组每一项的上界会是 \(\infty\),我们就没有一个明确的计数对象了。

一个手法是:考虑一个足够大的上界 \(M\ge nR\),我们不再对数组每一项的上界做限制,只去限制总和 \(\le M\),然后容斥的时候,枚举有 \(x\) 个人是 \(\gt R\) 的,那么额外加数就是 \(x(R+1)+(n-x)L\)

枚举 \(x\) 以后加数即为常数 \(B\),然后为了方便起见,不妨设 \(M=10^{len}-1\),这样的话我们就是固定一个 \(O(\log v)\) 级别的长度,然后钦定 \(\sum a+b\) 的位数不超过这个长度即可。

此时就可以考虑数位 dp:设 \(dp(i,d,up)\) 表示,我们考虑完了最\(i\) 位,然后低位向这部分的进位值为 \(up\)(显然 \(up\le n\),因为只有 \(n+1\) 个数相加),在高的 \(i\) 位里,\(\sum D-\sum D^2\bmod m=d\),满足这些条件的方案数。

转移的时候:我们需要知道第 \(i+1\) 位的 \(n\) 个数的 digit sum,这个值 \(S\) 显然不超过 \(9n\),对于每个 \(S\),方案数可以预先 dp 算出,设为 \(g(S)\)。我们枚举 \(d'\)\(up'\) 还有 \(S\),这样的话,\(dp(i+1,d',up')\) 就能唯一地从对应的 \(dp(i,d,up)\) 转移过来。最后 \(dp(1,0,0)\) 即为答案

这样做,我们最外层的容斥有 \(n\) 次,每次 dp 的时候,状态数是 \(O(\log V\times nm)\) 的,转移的时候有 \(9n\) 的枚举量,所以计算量是 \(9n^3m\log V\) 的,实现得当或许可以卡过去,但我们还可以进一步地去优化:

我们枚举 \(d',up'\) 以后,不枚举 \(S\),而是枚举 \(r=S\bmod 10\)。注意到 \(d\) 的计算仅和 \(S\bmod 10\) 有关,所以我们就能推出 \(d\),但是不确定的是 \(up\)。注意到 \(up=\lfloor \frac{S+up'+C}{10} \rfloor\),其中 \(C\) 是加数 \(B\) 在第 \(i+1\) 位的值。因此假设 \(S=k10+r\),则 \(up=k+\lfloor \frac{r+up'+C}{10} \rfloor\)

注意到 \(r+up'+C\le n+2\times 9=38\),所以后面的这个式子取值范围是 \(0\sim 3\)

因此,对于每个 \(d,r,v(\le 3)\),我们预处理求出:\(\sum_{k=0}^{n-1}g(10k+r)\times dp(i,d,k+v)\) 即可。那么转移的计算量就变成了 \(n^3m\log V\),而预处理多出来的总计算量应为 \(27n^2m\log V\),可以轻松通过。

代码

牛客23 Round9 C

题意:

给出一个带权连通图(边权非负),每个点有点权 \(a_i\);构造一颗生成树,使得距离点 \(u\) 最远点的距离恰好为 \(a_u\),或报告无解。

\(n\le 10^5,m\le 2\times 10^5,a_i\le 10^9\)

这个题非常的趣味,首先乍一看有点难以入手:我们考虑先确定直径,显然直径长度就是 \(L = \max a_i\)

拉出一条直径,则对于不在该直径上的点 \(u\),考虑它的父亲(也就是邻居里,更靠近直径的那个)\(fa\),总满足 \(a_{fa}=a_{u}+w(u,fa)\)

我们把所有满足这样的 \((u,fa)\) 连有向边,得到一张图 \(G\),显然我们只会从 \(a\) 大的连向 \(a\) 小的。

对于直径上的点,是否也满足这样的关系呢?我们会发现,直径中点左侧的一个点 \(u\),它和它的后继满足这个关系;直径中点右侧的 \(u\) 会和前驱满足这个关系。

问题在于:直径的中点这里会出现一些问题,具体而言我们需要分类讨论:

  • 直径的中点落在顶点上,也就是存在一个点的 \(a=\frac{L}{2}\),那么它左右两边的两个点都应该连向它。
  • 直径的中点落在边上,也就是存在一对相邻点 \((x,y)\),使得中点落在它们之间;此时 \(x\) 这一部分的点应该是向右指向的,\(y\) 这一部分的点应该是向左指向的。但是 \((x,y)\) 之间无法建立上述的联系。

我们来对两种情况分别考虑,显然第一种看上去会比较容易。

那么这个中点 \(M\) 就应该是 \(a=\frac{L}{2}\) 的点,显然也应该是全局 \(a\) 最小的点。

我们相当于是,按照 \(G\) 的连边,从两个 \(a=L\) 的点开始,分别走出一条路径到 \(M\),要求这两条路径的点不交(除了终点同时为 \(M\))。考虑这是容易用网络流解决的。

再来考虑第二种情况:显然 \(x,y\) 有一者必须是 \(\min a\) 的那个点,不妨认为就是 \(x\)

则我们应该是,从两个 \(a=L\) 的点开始,一条路径走到 \(x\),另一条路径走到 \(y\),依旧要求两者不交。

但一个问题是我们还不知道 \(y\) 是谁?首先 \(x,y\) 之间肯定还得有连边,其次这个边权应该恰好就是 \(a_x+a_y-L\)。那么我们只用让终点是所有满足这个条件的 \(y\) 之一就行了,同样容易用网络流解决。

当利用了网络流找出直径上的所有点的后,对于不在直径上的点 \(u\),我们直接任意挑选一条 \(G\)\(u\) 的出边连接即可。那么就存在一个陷进:如果有一个不在直径上的点,还选不出出边怎么办?为了此,我们一开始考虑所有 \(y\neq x\) 且没有出边的 \(y\),如果没有这样的,我们就不需要考虑这个问题;否则就只能是这个 \(y\) 去和 \(x\) 配对(显然如果有两个那就无解了)。

最后注意到,由于我们的最大流是 \(=2\) 的,所以可以认为时间复杂度就是 \(O(n+m)\)

代码

牛客23 Round9 F

题意:

对长度为 \(n\),值域 \([1,m]\) 的数列 \(a\) 计数,并有额外约束:

  • 若干条约束,限制某个 \(a_x\neq a_y\)
  • 若干条约束,限制某个值的出现次数 \(f_x\neq y\)
  • 钦定了 \(\{f_1,f_2,...,f_m\}\) 构成的多重集 \(S\),以 \(0\le B_0\le B_1\le B_2\le ... B_m\le n\) 的形式给出这个多重集,满足 \(\sum B=n\)

\(n\le 16,m\le 30\)

很高质量的计数题。

考虑因为有若干 \(a_x\neq a_y\) 的限制,因此若不挨个枚举 \(i=1\sim m\) 并枚举 \(a=i\) 的位置,则比较难以处理这个约束;考虑在这样的 dp 的过程中,我们需要记录 \(1\sim n\) 哪些位置被占用,但还需要关注 \(f_1\sim f_i\) 构成的多重集的形态。很容易发现这个数量远不到 \(2^m\),且由于必须是 \(S\) 的子集的缘故,我们搜索一下,发现仍有一定数量:考虑一定是 \(0\) 贡献的最多,因为其他值的出现都是收到 \(\sum n\) 的限制的,我们不妨只考虑 \(f\ge 1\) 的元素构成的多重集,只要这个和 \(S\)\(\ge 1\) 的元素构成的多重集相等即可。此时再去搜索,发现可能的多重集数量就只有 \(72\) 个。令 \(M=72\)

然后很容易想到 \(dp(i,M,mask)\) 表示考虑完 \(\le i\) 的值的所有出现,多重集形态为 \(M\),被占用的位置为 \(mask\) 的方案数。转移的时候,最朴素的想法是 \(3^n\) 枚举子集,显然无法通过;即使是子集卷积,那也是 \(O(n^2mM2^n)\) 的,依旧无法通过。并且发现似乎没有什么优化余地了。

这时候有一个很厉害的技巧出现了:我们考虑记录 \(2^n\) 的状态,是为了让每个位置都恰好有一个数,为了此,我们就需要在转移的时候做子集卷积。考虑把“每个位置都恰好有一个数”容斥掉,首先它等价于每个位置都至少有一个数(因为一共只有 \(n\) 个数),那就考虑钦定一些位置没有数,这里直接 \(2^n\) 枚举。然后再来做 dp:直接设 \(dp(i,M)\) 表示考虑完 \(\le i\) 的值的所有出现,多重集形态为 \(M\) 即可。转移的时候暴力枚举 \(i+1\) 的出现次数,这里的转移系数需要预处理算一下,然后就做到了 \(O(nmM2^n)\),可以通过。

代码

Luogu P7596 游戏蛇

很困难的题目,怎么才紫?

首先来考虑只有一次询问该怎么做。

需要注意到,”有一个人拐进副链“是非常关键的时刻:率先拐进去的这个人(记作甲)往后的策略是确定的;然后我们来考虑乙:我们不妨假设甲是先手,拐进了第 \(i\) 条副链。那么分两种情况:第一种是乙到达 \(i+1\) 的时候甲还有一截蛇尾没有进入副链 \(i\) ,这种情况下,乙就一定会选择一个 \(\ge i+1\) 的副链拐进去,然后两人互不干扰;第二种情况是乙到达 \(i+1\) 的时候,甲的蛇尾已经在 \(i\) 或者 \(i\) 的副链上,这时,我们乙不断跟着甲继续往里面走就行了,此时是甲必败的。 对于甲是后手的情况,也有类似的分析。

现在,令 \(m=b+\lfloor\frac{c-b}{2}\rfloor\),我们看到:对于先手来说,如果它率先进入副链,则它应该位于 \([b,m]\) 之间;如果是后手的话,就应该位于 \((m,c]\) 之间。

考虑对先手求出最小的 \(i\) ,使得它率先拐进 \(b+i\) 可以获胜,记作 \(v_a\);类似对后手求出,最小的可以获胜的 \(c-i\),记作 \(v_b\)(若不存在,则记作 \(\infty\)),则有:

  • \(v_a,v_b\) 全为 \(\infty\),说明两个人都不会想拐,此时两条蛇会轮流往中心挤压,直到有一者无路可走选择拐,这个人即落败。因此此时我们只关注 \(c-b\) 的奇偶性。

  • 否则,若 \(v_a\le v_b\),先手就能抢先制胜;若 \(v_a\gt v_b\) 就是先手落败。

那么现在只需要求出 \(v_a\)\(v_b\)。以 \(v_a\) 为例,不妨先考虑暴力枚举每一个 \(i\in [b,m]\) 再加以判断。令 \(L=b-a+1\),也就是先手的长度:

  • 如果 \(x_i\ge m\),这条蛇就会完全拐进副链,这个时候就要注意不能在后手到达 \(i+1\) 前就拐完,也就是说:\(i-b+L\) 必须大于 \(c-i\)
  • 然后后手就会在 \(\ge i+1\) 的位置选择一条链拐进去,事实上并不是 \((i,c]\) 这个范围,这里有一个隐含条件:因为我们是假设先手在 \(i\) 处率先拐的,那么后手应该是和先手一起向里走了 \(i-b\) 步,那么先手选择拐进 \(i\) 了以后,后手就只能拐进 \((i,c-i+b)\) 中的一个点。

因此我们总结出:此时先手的总移动轮数是 \(x_i+i-b\),而后手的总移动轮数应该是 \(\max_{j=i+1}^{c-i+b}\{x_j+c-j\}\)。只要前者大于后者,那么 \(i\) 就是一个先手的必胜点。对于后手的寻找,也就是 \(v_b\) 的计算,也有类似的结论。

此时我们可以很容易地 \(O(n)\) 回答一组询问,视常数表现可以获得 \(20\sim 40\) 的分数。。

接下来就要考虑数据结构优化了:我们先不考虑当 \(x_i\ge m\) 的时候必须有 \(i-b+L\gt c-i\) 的限制(这条称为额外限制)。来考虑寻找一个最小的 \(i\),使得 \(x_i+i-b\gt \max_{j=i+1}^{c-i+b}\{x_j+c-j\}\)

移项后得:\(x_i+i\gt \max_{j=i+1}^{c-i+b}\{x_j+c+b-j\}\)

这个时候注意到,对于一个固定的 \(i\) 而言,我们只关注 \((b+c)\) 的值:\(b+c\) 越大,就越容易不合法。因此,我们若把询问按照 \(b+c\) 排序,则对于一个 \(i\) 而言,一定在一段询问的前缀里,它是合法的。

那么这个时候就很容易了:我们容易预先通过二分 + ST表来求出每一个 \(i\) 的最大的合法的 \(b+c\);然后按照 \(b+c\) 扫描询问,则等价于按顺序激活一些位置,查询的时候是在查 \([b,m]\) 内最小的被激活位置,容易使用 set 或者 dsu 找到。

现在考虑加入额外限制:它等价于,若 \(i-b+L\le c-i\),我们的 \(i\) 就还需满足 \(x_i\lt L\)。首先移项得到 \(2i\le c+b-L\),那么得到 \(i\) 的一个上界 \(p\)。相当于是先去 \([b,p]\) 找(要求 \(x_i\lt L\)),如果找不到再去 \((p,m]\) 里找。那么这是容易线段树二分的。这样额外限制也解决完毕。

对于求 \(v_b\) 显然有同样的做法,不再赘述。这样我们就在 \(O((n+q)\log)\) 的时间内解决了这个问题。

代码

正睿24省选十连 Round2 B

题意:给出一颗大小为 \(n\) 的数,并且定义其 dfs 序是从 \(1\) 开始:按编号从小到大考虑儿子并依次遍历的顺序。

然后有 \(q\) 次强制在线的询问。每次给出 \(k\)\(dfs\) 序上的不交区间 \([l_1,r_1],[l_2,r_2],...[l_k,r_k]\),询问:如果只保留 \(k\) 个区间内的点,形成的连通块个数。

\(n,q,\sum k\le 5\times 10^5\)

很精妙的数据结构题目!

考虑使用点减边来算连通块个数:则我们只需要统计有多少对 \((u,fa_u)\) 同时被覆盖到。

我们枚举区间 \([l_i,r_i]\)\([l_j,r_j]\),然后计算有多少个 \(u\) 满足 \(u\in [l_i,r_i]\)\(fa_u\in [l_j,r_j]\)

枚举 \(i,j\) 以后显然是一个二维数点问题:利用前缀和,可以转化成 dfs 序上一个前缀内 $\le $ 某个 \(r\) 的值的个数。

考虑先对 \(k\) 根号分治,然后 \(O(n\sqrt n+k^2)\) 地去回答二维数点的答案。为了强制在线地需要,要把分块可持久化下来。这个做法时间很劣且难以实现。

依旧考虑点减边的想法。我们先来研究 \(k=1\),注意到有这样一个做法:从 \(l\) 开始,设 \(dfn_u=l\),然后我们跳到 \(l+sz_u\)。重复这个过程直到会跳出 \(r\) 为止。然后每跳一次就意味着多一个连通块。用倍增维护这个跳跃过程即可。

对于 \(k\gt 1\) 的情形:首先每个段都用这样的倍增算出连通块数目,然后注意到不同的区间拆出来的连通块可能会合并,我么们来尝试考虑不同的 \([l_i,r_i],[l_j,r_j]\) 之间会产生合并的次数。

枚举 \([l_i,r_i]\) 的话,考虑计算其中有多少个点 \(u\),使得其父亲 \(fa_u\) 被另一个区间包含。注意到,令 \(dfn_x=l_i\),那么我们 \([l_i,r_i]\) 拆出来的所有连通块,根的父亲都一定在 \(1\rightarrow x\) 这条链上。换言之就是 \(fa_u\) 得在 \(1\rightarrow x\) 的链上。

然后说:我们不妨先对所有区间排序,然后从前往后按顺序考虑:这个过程其实就是和 dfs 的过程一致。类似维护 dfs 过程中的栈一样的角度,我们同样用一个栈维护先前的所有区间。

然后,考虑新加入一个区间的时候,旧有的顶部的一些区间就会被弹出。显然也只有这些区间会和 \([l_i,r_i]\) 有连边,所以我们边检查栈顶边计算 \([l_j,r_j]\)\([l_{top},r_{top}]\) 的连边数量即可。

这个实际上就是说,我们本来要对 \(k^2\)\((i,j)\) 都检查,但是通过合理的一些处理,只需要对 \(O(k)\)\((i,j)\) 检查了。

那么直接套主席树去做这个在线数点其实就可以,也可以使用倍增。 时间复杂度 \(O((n+\sum k)\log n)\)

代码

CTT2021 末日魔法少女计划

非常神的题!

首先注意到出现了 \(A^k\) 这种东西,套路地考虑转成图论意义。

那其实是这样:对于每对点 \((i,j)\),都要有 \(i\rightarrow j\) 的最短路不超过 \(k\)。初始 \(i\rightarrow i+1\) 有边,我们需要添加不超过 \(n\times f(k)\) 的边满足要求。若我们添加了 \(x\rightarrow y\),则必须满足存在一个中间点 \(z\) 使得 \(x\rightarrow z\)\(z\rightarrow y\) 都存在。

先来考虑 \(k=2\):我们可以考虑分治:\(l\sim mid-2\) 全部连向 \(mid\),然后 \(mid+2\sim r\) 全部被 \(mid\) 连到。

这样的话:我们其实是把序列分成了 \([l,mid)\)\((mid,R]\) 两段,然后第一段一定能在两步内走到第二段,所以我们只要递归下去:让 \([l,mid)\)\((mid,R]\) 内部合法即可。这样次数就是 \(T(n)=O(n)+2T(\frac{n}{2})=O(n\log n)\) 级别的。实现出来可以通过第一问,因为底层 \(r-l\le 2\) 的时候是不需要连边的。

对于 \(k\) 更一般的情况:考虑使用 dp 的方式来找到最优决策。

首先明确我们的问题:传入 \(n\) 个点 \(a_1,a_2,...,a_n\) 和参数 \(k\),保证 \(a_i\rightarrow a_{i+1}\) 的连边已经存在。加入尽可能少的连边使得满足任意两点的距离不超过 \(k\)。初始的时候 \(a_i=i\)。接下来

大体思路是:我们考虑选出 \(p\) 个分割点 \(c_1\lt c_2\lt ... \lt c_p\)。然后分割成了 \([1,c_1),[c_1,c_2),...,[c_p,n+1)\)\(p+1\) 个段。\([1,c_1)\) 的每个点连向 \(c_1\)\([c_p,n+1)\) 的每个点被 \(c_p\) 连向。对于每一个 \([c_i,c_{i+1})\),内部的点全部连向 \(c_{i+1}\),也被 \(c_i\) 连向。

我们还需要做到:

  • \(c_1,c_2,...,c_p\) 这些点之间,任意两点的距离不超过 \(k-2\)
  • 每个段内部的点距离不超过 \(k\)

两部分都容易划归成子问题(注意到第一部分里,\(c_{i}\)\(c_{i+1}\) 已经新添了连边),递归下去做即可。因此只记录 \(f(n,k)\) 是足够完成 dp 转移的。

但这样的话转移的时间会爆炸。

感受一些最优解的形态:首先前后缀的长度应该比较相近,然后中间的 \(p-1\) 个段也应该较为均匀。

因此我们考虑加强限制:前后缀区间的长度相等,同时中间的 \(p-1\) 个段里,任意两个段的长度差不超过 \(1\),也就是平均分配。

这样的话,我们先枚举两侧区间的长度:然后考虑设一个辅助状态 \(g(n,k)\) 来解决中间的划分。而 \(g(n,k)\) 的转移只需要枚举段数即可。两部分都容易在 \(O(n^2k)\) 的总时间内完成转移。

这样直接实现会在 \(k=3\) 差一点分数:如果实现出来也容易注意到,\([1,c_1)\)\([c_p,n+1)\) 是有区别的:前者递归到 \([1,c_1)\),后者递归到 \((c_p,n+1)\)。因此感性上来看后缀可以比前缀略长一点。我们只需要再考虑后缀长度是前缀长度 \(+1\) 的情况即可。

代码

XX Open Cup,Grand Prix of Kazan,J. Jiry Matchings

比较套路的题目。

首先我们感知到答案是有凸性的,所以如果是序列上的问题,就可以考虑分治闵和。

在树上怎么分治闵和呢?这是一个类似树上背包分治NTT的东西。

其实和NTT没任何关系。考虑我们只关注根节点处的背包信息,因此有这样一种手法:树剖以后,对于每条重链顶 \(u\),我们记录 \(u\) 子树内的信息为 \(f(u)\)。则所有 \(f(u)\) 的大小之和根据 dsu on tree 的分析是 \(O(n\log n)\) 的。

如何快速计算 \(f(u)\)?我们拉出 \(u\) 所在的重链:然后看成是序列 \(a_1,a_2,...,a_m\)。对于每个 \(a_i\),先将所有轻儿子的 dp 信息卷起,挂在 \(a_i\) 上。然后我们再在这条链上做分治卷积即可。

这是大体的思路:在轻儿子卷积的时候,我们可以用 huffman 树减小常数(huffman 树似乎是一个很优的东西)。还有一个细节问题是:在链上卷积的时候,我们应该删除前缀的不可达状态(这个长度大概是 \(O(1)\) 级别的),否则序列的凸性会被破坏。如果没有这样做大概会 wa on 8。

这样整个问题就在 \(O(n\log^2 n)\) 的时间内得到解决。

代码

CF1229F Mateusz and Escape Room

先考虑链的情况怎么做。

环上传递有一个比较经典的手法(与HAOI2008 分糖果类似):我们可以设 \(a_i\)\(i\rightarrow i+1\) 传递的数量,如果为负表示 \(i+1\rightarrow i\) 传递。然后就是最小化 \(\sum |a_i|\)

这个做法在有费用流模型支撑后其实是比较有力的:就是 \(s\rightarrow i\) 连边容量 \(a_i\),每个点向相邻点连边,容量无穷费用 \(1\),每个点向 \(t\) 连边,容量 \([L_i,R_i]\),是一个上下界的东西。

能建出费用流模型的问题,凸性质都会比较优秀:首先我们设 \(f(i,j)\) 表示考虑完了 \(1\sim i\)\(a_i=j\) 的时候的最少传递次数,则在定义域内 \(f(i,j)\) 关于 \(j\) 下凸。

证明这点不需要使用费用流,归纳即可:我们转移的时候,相当于先 \(f(i-1,j)\rightarrow f(i,j+a_i)\),然后再 \(f(i,j)\rightarrow f'(i,k)(j-R_i\le k\le j-L_i)\),最后再 \(f(i,j) := f(i,j)+|j|\)。第一步是平移,第三步是加绝对值函数。第二步也是比较经典的内容:相当于我们找到凸壳的下顶点,左侧凸壳向左平移 \(R\),右侧凸壳向左平移 \(L\),中间的部分用谷底的值补全(相当于平移的同时,极点被拉伸了 \(R-L\) 的距离)。

然后考虑用 slope trick 来维护就能在 \(O(n\log)\) 的时间内解决链上的问题。

对于环上的情况,尝试断环成链。一个结论是费用流不光关于总流量有凸性,关于一条边的流量也有凸性。所以我们考察 \(n\rightarrow 1\) 的流量,然后直接接在凸壳上二分斜率找到最优秀的 \(a_n\)。这样时间复杂度就是 \(O(n\log^2)\) 了。到这里这个题就结束了。

为什么费用流关于一条边的流量也有凸性呢?我们考虑连接 \(t\rightarrow s\) 的边,然后断掉 \(u\rightarrow v\),以 \(u\) 为汇点 \(v\) 为源点即可。当然,因为费用流的凸性是在 \([0,maxflow]\) 之间的,所以我们在凸壳上二分的上下界也应该是 \([0,maxflow]\)(显然本题的 \(maxflow\) 就是 \(\sum a\))。

代码

XX Open Cup,Grand Prix of Kazan,H. Honorable Mention

也是比较典的题目。

首先这个询问的东西大家都知道有凸性了。并且如果求全局答案也很显然可以闵和。但是现在是对区间求答案了,咋办?

我们 wqs 二分一手!然后区间在线段树上拆成 \(\log\) 个节点,我们就只关注 \(\max dp(i,x)-kx\) 这种东西了。

所以总体思路是:线段树上闵和预处理一手,然后对每个节点建凸包。查询的时候先 wqs 二分,然后对每个节点在凸包上二分一下,最后得到的 \(O(\log)\) 信息合并一下。时间复杂度 \(O(q\log^3)\),已经可以通过。

离线下来套一个整体二分可以做到 \(O(q\log^2)\),但我没写。

代码

XX Open Cup,Grand Prix of Tokyo,B. Evacuation

这个题很好!

首先,对于一次询问,我们注意到如果起始点 \(x\le mid\) 则我们并不关注 \(r\),否则我们并不关注 \(l\)。这是一个很好的事情。

\(f(l,x)\) 是只有左侧限制的时候,起始点在 \(x\) 的答案;类似设 \(g(r,x)\) 表示只有右侧限制的情况。

我们要支持的就是求 \(f(l)\) 的区间最大值这种东西。

首先如果我们要求的是全局最大值那有个很容易感知到的东西是决策点一定关于 \(l\) 单调升。

现在是区间,我们拆成线段树上的 \(\log\) 个节点,每个节点分别跑决策单调性就好了。

时间复杂度是 \(O((n+q)\log^2 n)\),很有实力!

听说有个神秘牛逼东西可以少一个 \(\log\),更有实力了。

代码

WC2020 选课

先来考虑 \(p=0\) 的情况,此时显然我们对每个类别考虑,再背包合并。

\(L=T-\sum_{i=1}^{m}s_i\),显然我们对于第 \(i\) 类,选出的学分上界应该在 \(O(s_i+L)\) 级别。

所以如果我们新求出获得 \(s_i+k\) 点学分的最小消耗 (\(k\) 的上界是 \(O(L)\) 的),我们直接 \(mL^2\) 背包合并即可。

注意到这里,学分只有 \(1/2/3\) 三种。对于这种体积 \(\le c\) 的背包,有结论:模 \(c!\) 同余的位置是凸的。

所以我们容易 \(O(nL)\) 地求出所有类别的 dp 信息。

对于 \(p\gt 0\) 的情况,我们考虑先 \(2^p\) 枚举。然后我们不需要每次重跑 \(m\) 个物品的合并,最多 \(p\) 个物品需要重新合并,剩余的 \(m-p\) 个没有被限制涉及到的类别直接预处理出来即可。

时间复杂度 \(O(nL+mL^2+2^ppL^2)\)

会做这个题并不困难,比较难的是实现。非常考验代码能力。

代码

posted on 2023-12-09 12:07  Cry_For_theMoon  阅读(294)  评论(3编辑  收藏  举报