record 12.18-12.22
zroi B 三项式
对于一个序列 \([a_1,\dots,a_n]\),\(a_i\in[l,r]\),求 \(s=\sum a_i\),\(s\) 的各个数位和与各个数位平方的和在模 \(m\) 意义下同余的方案数。
\(n,m\le 20,0\le l\le r\le 10^{1000}\)。
看题解补的。
我先说一下赛时想法。
这个东西可以分成两部分,一部分是找出合法的 s,一部分是对这样的 s 求满足条件的序列个数。
我们先做后一部分。
这个是典中点容斥对吧,写出来就是
因为我们找出合法 s 大概率是要通过一个数位 dp 来做的,所以我们统计答案的方式也要能够通过数位 dp 的方式更新。你可以当成我们目前知道 \(\sum\limits_{s\in S}f(s)\),怎么快速求出 \(\sum\limits_{s\in S}f(s+a)\) 是我们关心的。
我们尝试把上面这个式子拆一拆。
我们发现,我们的主要任务转化成了对每个 i,k 求 \(\sum_{s}(s-nl-i(r-l+1)+n-1)^k\)。
这个东西你发现就是多项式对吧,问题转化成求 \(\sum_s s^0,\sum_s s^1,\dots,\sum_s s^{n-1}\)。
这个东西是可以加的,一次加操作可以在 \(O(n^2)\) 时间内完成。
倘若就这,那么我们可以做 \(\log r\times m\times 10\times n^2\)。是 8e7 的。
但问题是我们求 \(\sum_{s}(s-nl-i(r-l+1)+n-1)^k\) 时要保证里面的东西是 \(\ge n-1\) 的,这就要求我们去把当前记录的 \(s\) 再细分出若干集合表示它们与这些关键位置的相对大小,也就是说我们要再记 \(O(n)\) 个状态,复杂度无法接受。
当然,我们也可以考虑 n 次 dp \(s-nl-i(r-l+1)\),这样就没有下界了,只需要保证 \(+nl+i(r-l+1)\) 是合法的 s 即可。
后面不会了。
关于题解:
题解用的是枚举 \(a_i\) 而非枚举 \(s\) 的做法。它考虑每次枚举 \(a_i\) 在这一位上的数位和,然后往上进位。
应用跟上面同样的容斥,我们只需要 dp n 次,每次只关心总和是否到达上界。这个是 \(O(mn^4\log r)\)
然后你发现状态数是 \(O(n\times m)\),转移数是 \(O(nm\times 10n)\),观察到你枚举的当前数位和尾数均为 \(p\) 时转移比较类似,所以求和同步转移,这样就又去掉一个 n,是 \(O(mn^3\log r)\)。
感觉理解还是很浅。为什么这种 dp 方式就能优化,我上面那种就不行呢?
zroi C 三分图
看题解补的。
考虑原图的一棵 dfs 生成树,我们对其黑白染色。
若没有部分的大小 \(<n\),那么我们可以考虑从中分出一个 \(n\) 出来,使得最终满足前两个条件。我们相当于要选出一些点,不能相邻,并且剩余两个部分剩下的点要都是 \(n\)。
我们考虑从下往上做,贪心选择这样的点。考虑到你选一个点之后,相当于是用了一个当前集合的点,ban 了一个另一个集合的点,所以这样贪心做一定可以找到可行解。
若有部分的大小 \(<n\),那么考虑满足后两个条件。我们选出所有的叶子。首先叶子之间一定没有连边,考虑反证法即可。
接下来我们要说明叶子的点数是最多的。我们考虑把整棵树剖成若干个不交的链,每个链由叶子开始向上一直往上顶,谁先开始并不重要。考虑这样的每个链,最多能为两部分的点数差贡献 1,所以叶子的点数是最多的。
这玩意是怎么想出来的?首先是这个题肯定是找充分条件而非充要条件。
我觉得应该是先基于第二个条件,发现要满足它的一个充分条件是 dfs 树上没有两个相邻的同色点。然后考虑去满足第一个第三个条件。
理解还是不充分啊!
THUPC 2024 初赛 D
给你长为 \(n\) 的 01 串,每次你可以选择一个位置 \(x\),如果 \(a_{x-i}\neq a_{x+i}\),就可以将 \(x\) 及较短的那一侧删掉。最少删多少次。\(n\le5000\)。
先考虑区间 dp,枚举 x 之后判断是否合法,这个是 \(n^4\)。
发现一个位置是合法的,当且仅当最近的那一对使它不合法的没有出现。我们记录这个半径,这样可以做 \(O(n^3)\)。
我们观察一个区间 \([l,r]\) 能对谁产生贡献,一个显然的事情是这样的区间一定有一个端点是跟它重合的,我们不妨设这样的区间是 \([l,r']\),那这样的话,我们选择的位置就是 \(r+1\)。
观察到首先要 \([l,r]\) 是较小的区间,这会给出 \(r'\) 的一个下界。其次 \(x\) 要是合法的,这会给出 \(r'\) 的一个上界。所以合法的 \(r'\) 是一个区间。我们立刻得到一个 \(O(n^2\log n)\) 的做法。
其次,我们发现这个 \(dp\) 数组的值域是 \(O(n)\) 的,所以我们可以按 len 为批次转移,每批当中把这个东西离线下来,做桶排,然后填充。
复杂度 \(O(n^2)\)。
呃呃,fqh 没有把题意给全。如果还要算另一个 dp 的话,是没办法开桶的。
但是他也没有告诉我 \(a_i\in\{0,1\}\)。=_=。
有了这个条件之后,我们发现一个比较显然的事情:决策点越靠中间越好。
你还是按长度计算区间的答案,同时在每个点处记录当前最好的决策点是谁就可以了。
Ynoi2014 人人本着正义之名
这个首先一眼看出来后面那些乱七八糟的操作是对极长 0/1 区间进行端点左右平移。
这个东西一看就很难做对吧,并且我们通过观察发现,一个点的颜色可能会被改很多次(我一次左移一次右移轮着来),但是颜色段之间的合并/删除等操作不会太多。所以我们可以考虑颜色段均摊。
用平衡树维护,每个节点代表一个极长颜色段。我们发现有几种情况会修改颜色段:区间覆盖,一个颜色段长度变成 0,两个同色颜色段扩张到了一起。
区间覆盖很简单。
颜色段长度变成 0,这种操作只会有 \(O(n)\) 次,所以每次我们可以 \(O(\log n)\) 找到这样的点删除即可。
同色段合并,这一定伴随着原本把它们分开的颜色长度变成 0,我们可以顺便处理。
CF1913 D
给一个无相同元素的序列,你可以进行任意次操作,每次操作选出一个区间,把这个区间内除了区间最小值的元素都删除,问最后能删出多少种不同的结果。
\(n\le3\times10^5\)。
我们肯定先要观察一下有没有什么性质。
首先,最后相同的结果,可能有很多种不同的删除方式。
并且,交换操作的顺序,可能会改变最终的结果。你考虑我这个操作本来的最小值是 \(x\),结果你把一个操作交换到我之前,把 \(x\) 给删了,那我的最小值肯定就变了。
这么一看好像啥性质都没有。我们考虑一些特殊的数。
对于全局最小值,它一定不会被删除,并且所有经过它的区间一定会把其它的数全都删掉。
对于全局最大值,如果它没有被删除,那所有操作一定不能经过它。
我们发现这二者都有一些比较适合分治下去的性质。
你别急,我们考虑反面,计数哪些东西是不可能被删出来的。
首先是最小值被删掉的,其次是最小值没被删,但是次小值被删,并且这俩东西之间有东西没被删。
同理,如果第 \(i\) 小没被删,但是第 \(i+1\) 小被删,这时想要不合法,需要第 \(i+1\) 小到左右已被激活的点之间都要有点没有被删除。这些东西都是可以算的。
我们写一发。
upd:假的。
我先记录一下接下来几个可能的方向:
笛卡尔树。
分治,按最大/最小值切开,跟上面那个好像没啥区别。
难点都在于分开之后不成子问题。
逆过来,考虑不合法的方案数。
我又想到一个做法:我们设 \(dp_i\) 表示仅考虑前 \(i\) 个位置,并且 \(i\) 没有被删除的方案数。
转移的时候,我们考虑紧挨着的上一个没有删除的位置是 \(j\),这要求 \(dp_i/dp_j\) 比 \((j,i)\) 都要小。
最后加一个虚拟点,不妨让它是 -1,答案就是 \(dp_{n+1}\)。
然后我们看一看稍微优化一下就好了。
看了一天了,终于做出来了!!!
CF1340 F
给一个多种括号组成的括号序列,支持单调修改,查询区间是否合法。
\(n\le10^5\)。
首先,对于单种括号的情况,我们有非常经典的做法:你考虑把一个区间能消就消,消完之后,要么这个区间根本不可能合法,要么剩下的是若干个左括号后面拼若干个右括号。那么我们就可以线段树维护了。
对于多种括号的情况,我们仍然有类似的结论,但是在合并区间的时候,我们需要判断一堆左括号能否跟一堆右括号匹配。这个东西考虑 hash,但是你发现我们需要求出形如“左子区间的后缀左括号 hash”“右子区间的前缀右括号 hash”。
我们考虑类似于楼房重建的线段树,重新写一个函数负责计算这个东西,以后缀左括号为例。
先通过记录长度,判断是否完全落在左子区间,是的话可以递归过去。否则,相当于是还有一部分在右子区间,但是这一部分右区间的左括号还要和左边的右括号抵消,所以向右递归,长度设置为 原长度-左区间左括号长度+左区间右括号长度,然后抵消,再把左区间的左括号拼到前面。
这样复杂度就是 \(O(n\log^2n)\)。
同时,还有一种做法。我们直接使用可持久化平衡树来维护 hash 值即可。
Luogu P6864
观察到这个取消的东西,其实就是把一个操作 ban 掉或者重新激活。
我一开始的想法是建一个括号树出来,然后维护这个树的形态,发现这玩意显然很难维护。
然后就恼了,怒看题解。看到一个矩阵,会了。
我们注意到每一次操作的增量,只跟当前这个序列有多少个 (...)(...)(...) 有关,我们考虑把一次操作改写成一个矩阵,然后取消操作其实就是暂时把一个矩阵删掉。
记录 \(\begin{bmatrix}ans&cur&1\end{bmatrix}\) 即可,操作 1 是 \(\begin{bmatrix}1&0&0\\0&0&0\\1&1&1\end{bmatrix}\),操作 2 是 \(\begin{bmatrix}1&0&0\\1&1&0\\1&1&1\end{bmatrix}\)。
CF1913 E
这个 \(n,m\) 都很小,我们考虑 flow。
那我们现在有两种建模的方法。第一种是我们以原 inp 为基础,考虑修改。你发现这样困难的地方在于修改一个点不一定对它的行列两个点都有正面作用。
那我们考虑先把所有点都当成 0,然后现在是有一些点选了要花钱,有一些点不选要花钱,那我们把后者变成选了可以挣钱就行了。
Ynoi2018 天降之物
显然这个操作一它要是只改改名字是没有用的,它有意义就必须得合并两个集合。这个可以用启发式合并,保证每个位置只被合并 log 次。
我们把这些集合根号分治,如果没有修改,小块小块暴力,大块对整个序列预处理即可。
考虑加入修改操作。如果合并的是两个大块,那我们再次暴力预处理,因为大块只有根号个,所以这么做没问题。
如果合并的是两个小块,如果合并之后还是小的,那合并之后扔那就行了,合并之后是大的,那就暴力预处理。
如果合并的是一大一小,那我们就要好好想想了。最坏情况,就是你只有一个最小的大块,剩下全是 1 的小块,然后一个个合并上去。这个如果我们真的合并显然难以接受,所以我们考虑延迟合并。具体来说,对每个大块外挂一个小块,查询的时候把两部分查询取 min,合并的时候,先跟外挂的小块合并,如果爆了再重构。
这样做总复杂度 \(O(n\sqrt{n})\)。
感觉这么做其实没咋利用这个距离 min 的性质,这东西感觉有些说法的。
看了题解区,发现说有序列分块做法,写一下。
序列分块之后,把答案分成块内部和块间。考虑一次查询,块间距离 min,我们只需要维护块内每个颜色出现的最左/右位置。
考虑一次合并,最左/右位置很好维护,块内部答案的话,我们直接暴力合并,这样每做一次,这个块内的颜色种类数就会减少 1,所以一共只会做 n 次。
AGC 012 D
你考虑把每个球看成一个点,上面的标号是它的位置。然后你把可以交换的两个球之间连边,你发现一次交换操作,就是交换标号,所以一个连通块内标号任意。我们现在的问题就是求出每个连通块的大小。
因为我们连的是一个前缀,所以我们考虑优化建图,但是你发现对于同种颜色和不同颜色的,要求还不太一样,所以你要构出一个不同颜色的对吧,那这个可以在颜色上做前缀后缀和就可以了。
复杂度 \(O(n\log n)\),瓶颈在排序。
这个不太对,并查集不能优化建图。
我们发现优化无非是两个方向:一个是该连的边我们都连,但是通过类似数据结构的方式偷懒;一个是在等价的前提下,减少需要连的边的数目。
我们考虑后一个方向。那同色内部我们显然只需要让最小的那个出去去连就行了。现在相当于缩了一些连通块了。然后异色的,我们考虑让全局最小的点去连边就可以了。
CF 710 F
这个东西我们用二进制分组来做,但是你发现删除操作其实很困难,不过注意到这个答案满足可减性,所以我们可以开两个 acam,分别二进制分组就可以了。
记录这个题是说一下二进制分组的写法。我们可以开一个栈,维护当前每一组的大小,然后每次往最后插入一个 1,并且如果最后两个相等,就把长度加起来,然后再 del & build。
THUPC2024 初赛 B
给一棵树,要把恰好 \(k\) 个点染成黑色,定义边权是两侧连通块内黑点个数差的绝对值,总权是边权求和,最小化这个值。
\(k\le n\le5\times10^5\)。
你发现一个子树内如果有 \(k'\) 个黑点,那么这个子树的根到父亲的边的贡献就是 \(|k-2k'|\)。
进一步,你发现对于合并而言,是先把子树的答案合并,然后再考虑根到它的父亲的贡献。而这些都是下凸包,并且合并是闵可夫斯基和,最后合并出来还是下凸包。
我们考虑维护差分数组,现在要支持差分数组的归并。你看一下这个“根到父亲的贡献”的差分数组,虽然每个位置都有数,但是都是一段一段的,所以我们用平衡树维护这个,差分数组的归并就是启发式合并,根到父亲的贡献,我们可以区间加。这个是 2 log 的。
但是为啥不 slope trick 呢?我觉得没问题。我要写一下这个。不行啊,merge 复杂度没保证。
但是这个平衡树显然很不好对吧。所以 sol 里给了一种神秘的做法。我们注意到贡献部分是分成至多三段的,我们相当于要找一种支持合并、分成三段的区间加的数据结构。它就是——可并堆!我们开三个可并堆,多维护一个全局加标记,然后使用类似于对顶堆的方式就可以了。这个是 1 log 的。
关于三元/四元环
主要是说一下这个很厉害的给图定向的方法。
我们考虑算出每个点的 \(deg\),然后给点按照 \(deg\) 排序,并且把每条边定向成从左往右。
我们现在考虑每个点的 \(odeg\),显然有 \(odeg_i\le deg_i\),对于 \(deg_i\le\sqrt{m}\) 的点,就证完了。对于 \(deg_i>\sqrt{m}\) 的,这样的点不超过 \(\sqrt{m}\) 个,而只能向右连边,所以这部分也是 \(odeg_i\le\sqrt{m}\)。
这个很厉害啊!
然后说一下它怎么计数的。我们计数这些环的基本思路都是折半。
你比方说三元环,我们钦定在最小的点处统计。对 \(u\),每次枚举有向出边 \((u,v)\),再枚举有向出边 \((v,w)\),然后给 \(w\) 打标记。再次枚举 \((u,w)\),加上 \(w\) 的标记即可。
你比方说四元环,发现定向之后有 3 种情况,你可以分每种情况来做。
同时,网上流传有一种做法。我们考虑在最大的点的对面处统计,每次枚举无向出边 \((u,v)\),再枚举有向出边 \((v,w)\),同时要求 \(deg_u\le deg_w\),然后往上打标记。之后再来一次。
说白了就是我们可以保证 \(\sum ideg_i\times odeg_i\) 以及 \(\sum deg_i\times odeg_i\) 的复杂度,那我们就往这个上面去凑。
那这个东西有什么用呢?
gym103470 K
容斥,发现剩下的情况都可以转化转化来做。
1st Universal Cup K
计数 \(K_4\)。
我们对每个点暴力找出 \(K_3\),然后每次建一个小图,连边表示有 \(K_3\),现在要在这个小图上找 \(K_3\)。
我们能不能再用一次类似的方法呢?不行。
因为一次的复杂度是 \(m_i\sqrt{m_i}\),而我们考虑一条边会进入小图当且仅当它是一个 \(K_3\)。所以 \(\sum m_i=O(m\sqrt{m})\),再乘 \(m_i\) 就爆爆爆了。
考虑使用 bitset,可以做到一次 \(O(\frac{n_im_i}{w})\),而 \(n_i\le\sqrt{m}\),所以总复杂度 \(O(\frac{m^2}{w})\)。
我们还是类似于把这个 \(K_4\) 放在 deg 最小的地方统计,然后把这个图拆拆拆。
THUPC2024 初赛 I
你要构造出一个给定的集合 \(T\),每个元素都 \(\le 5\times10^5\)。你有如下操作可以使用:
新建一个大小为 1 的集合,值任意。
合并两个不交集合。
把一个集合里每个元素都加上 \(x\)。
其中每次操作都保留历史版本。要求操作次数 \(\le10^6\),历史版本的元素个数和 \(\le5\times10^6\)。
我们第一想法肯定就直接上分治,然后直接合并就行了。但是你发现元素个数恰好是要求的 2 倍。=_=
那肯定要想想怎么利用操作 3 对吧。一个自然的想法是,你在分治的时候把右边平移过来,两边能重合的部分单独搞出来,然后再去递归。但是可以被卡,只要让两边没有重合部分就行了,这样你每次递归的询问个数都是区间长度的一半,相当于没优化。但是听说对分治中心随机扰动就过了。
那我们还有一个想法对吧,我们在分支的时候求一下两边最多重合多少,然后再分治。但是复杂度我不会算,可能不太对。
这个时候其实你看一下操作 3 有什么用。它相当于是把 pattern 相同的段复制到别的地方去。那我们其实可以考虑把整个序列 gank 成若干段,然后去复制。
不妨设我们的段长是 \(B\),那么这一共有 \(2^B\) 种情况,为了凑出来,我们需要 \(B2^{B-1}+B\) 次操作,最后的总代价是 \(B2^B\)。
然后我们开始平移。一共有 \(\frac{n}{B}\) 段,每段都需要一次平移操作,这又是 \(\frac{n}{B}\) 次操作以及 \(n\) 的代价。
接下来用线段树的方式合并。需要 \(\frac{n}{B}\) 次操作以及 \(n\log\frac{n}{B}\) 的代价。
但是这样也不对啊,没看懂。以后再看吧。
THUPC2024 初赛 J
我们考虑一个数 \(x\) 对哪些 \(k\) 而言存在一个长度为 \(k\) 的区间,使得 \(x\) 是这个区间的 mex。
你考虑到这要求 \(x\) 没有出现,所以这就把整个序列切割成了若干个小段。
你再考虑这要求 \(0\dots x-1\) 都要出现,所以对每个小段,这个满足条件的 \(k\) 都是一个区间。
如果我们知道这些 \(k\) 的区间,那我们在线段树上,区间 chkmn,单点查询就可以做了。
那么问题转化成怎么找出这些 \(k\) 的区间。考虑对 \(x\) 按从小到大枚举,当 \(x\rightarrow x+1\) 时,我们考虑更新。你发现这个是对一个区间,有一条直线,对它取 max,然后查询是区间 min。
这个我先胡一个。因为直线斜率都是 1,所以我们把原数组的 \(a_i\) 改写成 \(i+b_i\) 的形式,然后去维护这个 \(b_i\)。你考虑这样做,对直线取 max,其实就是对 \(b_i\) 区间 chkmx,这个可以 segtree beats,转化成对 \(b\) 区间加。区间取 min,就是对区间 \(i+b_i\) 求 min,在有区间加操作的情况下,这个东西可以维护凸包。
这做法太抽象了。我们看一眼 sol。
好好好,思路跟 sol 高度重合。
最后一部分,我维护的相当于是每个右端点,想让它 \(0\dots x-1\) 都出现的长度最小值。题解里维护的是对应的左端点。这样有什么好处呢?欸你发现这个序列是单调的。所以原本的区间 chkmn 操作可以先二分,就变成区间覆盖了。然后要求的东西是 \(i-p_i+1\) 的 min,这个在区间覆盖的情况下也是很好做的。
总复杂度 1 log。
套路世界大战。
btw,已经会了 9 个题了。/ku
Luogu P8564
给一棵以 1 为根的树,每次可以选一个点,把它的子树内除了它以外的都删除,代价为一个跟子树大小有关的值。求删到只剩根最小代价。
\(n\le5000\)。
发现操作是顺序无关的。我们从下往上执行,dp 顺序也是如此。根节点最后一定要删一次,你发现这个代价只跟它的子树的剩余个数和有关。进一步,你发现以任何点为根的子树也是这样的。
所以我们记录状态可以考虑记录子树剩余大小。然后合并就行了。
HEOI2013 SAO
给一棵树,边有定向,求这棵树的拓扑序计数。
\(n\le1000\)。
我们考虑怎么设计状态。比方说有一个根,然后有一些儿子是连向它的,有一些儿子是被它连的。你发现这个只对根节点和子树根节点之间的拓扑序有要求,那我们不妨记录一下根节点的拓扑序。
接下来考虑合并,我们每次合并上去一个子树。这实际上是合并两个有序序列,要求每个序列内相对顺序不改变,并且对其中两个元素的顺序有要求。如果没有额外要求,我们可以插板对吧。现在我们考虑修正一下。
比方说两个序列分别长 \(n,m\),其中根节点位置在 \(u\),子树根节点位置在 \(v\)。我们让 \(m\) 去插 \(n\)。
你考虑最终 \(u\) 之前插入了至少 \(v\) 个点,你去枚举这个东西。
这个后面可以前缀和优化,所以我们就做到了 \(O(n^2)\)。
之前一直在想怎么优化最上面那个式子,但是其实我们要算的不是那个,而是乘一些系数之后再贡献到不同的位置。
另外,这个模型还有一些别的组合意义。虽然这个题用不上。
你考虑枚举第 \(v\) 个板之前有多少个点,这个是至少 \(u+v\) 个点。
同样的,我们也可以考虑 \(u+v\) 个点的位置前面有多少个板,这个是至多 \(v\) 个。
这两个组合意义上显然是等价的,但式子写出来其实不太一样,盒 这个题就利用了这两种不同的写法,在枚举的过程中 \(O(1)\) 更改这个式子的值。
十二省联考2019 皮配
首先我们给出一个 nt 做法:\(dp_{n,a,b,c,d}\) 分别记录四个和,这样复杂度是高贵的 \(O(nM^4)\)。
接下来我们进行一些观察。首先,\(a+b=c+d=\sum s_i\),这说明我们有两个和不用记录,复杂度立刻下降到 \(O(nM^2)\)。可以获得 50 pts。
然后呢?我不会啊!
我们手动降一下难度,先不考虑有偏好的学校。你发现你现在的 dp 是形如 \(dp_{n,a,b}\),每次可以在 4 种加到 \(a,b\) 上进行选择。然后你发现这个转移是有规律的,你想能不能使用一些神奇的数据结构优化,但是并不行。
你觉得如果两个维度是独立的就太好了。
你发现,我们实质上是在做一个这样的过程:\(\sum_S\sum_T\text{valid}(S,T)\)。其中 \(S,T\) 你可以看成是二进制串,分别枚举每个元素属于哪一行/列。
最开始,我们认为 \(\text{valid}(S,T)=[\sum_{i\in S} s_i\le a][\sum_{i\not\in{S}}s_i\le b][\sum_{i\in T} s_i\le c][\sum_{i\not\in{T}}s_i\le d][\text{同城市对 S 的限制}]\)。
但是我们发现两个条件是多余的,可以写成 \(\text{valid}(S,T)=[a\le\sum_{i\in S}s_i\le b][c\le\sum_{i\in T} s_i\le d][\text{同城市对 S 的限制}]\)。基于这个东西 dp,就得到第二个算法。
但是我们一眼就可以看出来,这个东西是 S T 互相独立的。所以:
复杂度降到 \(O(nM)\)。
接下来考虑有 \(k\) 个有偏好的学校,这个显然 S T 就不独立了。
这个怎么办呢?我们肯定把那些没有限制的单独算一下,这部分就只好去枚举被影响的学校的选择情况,可以考虑容斥对吧。
然后你大概想一下就发现容斥跟不容斥没啥区别。我们考虑枚举这些学校的 S 的选择情况,然后你一个个条件看带来了什么影响。你发现一些城市的 S 情况被确定了,所以第一个条件的范围要改改。第二个条件,这些学校可能选择 S 之后 T 的选择也固定了,范围要改改。同时你还要注意,范围改了之后,有一些东西就不参与之前那个“没有限制的”。
或者说,我们还有一种理解方式。你发现这个式子在说,有 \(k\) 个 T 跟 S 扯上了关系,但是其它 T 还是相对而言独立的。所以我们可以把其它 T 单独做一个 dp,把那些没有牵扯进来的 S 做一个 dp,然后把 \(k\) 个 T 跟被牵扯到的 S 一起 dp。
然后你发现后面这个 dp,它的两个维度,一个是 \(ks\),一个是 \(M\),复杂度就是 \(O(k\times ks\times M)\)。
总复杂度 \(O(nM+k^2sM)\)。
我想说的就是,我们的 dp 方程,是随着我们对这个题目性质的认识逐步改变的。再有就是我们要坚定信念,你看原本我们可以拆成两部分独立的东西,加上限制之后出题人肯定舍不得破坏这么优美的性质,所以一定也有某种意义上的独立性。还有就是,适当的转化可以便于你观察理解。你像我们把这个东西用 01 串表示之后,原本模糊主观的独立的感觉,就变得严谨又直观。再有就是,盯着看不如实际去试一试,走一走。

浙公网安备 33010602011771号