P13725
采取神人的方法:考虑求 \(q(s,t)\) 表示一个 \(f(P)=s,f(Q)=t\) 的答案,然后做容斥。
答案肯定是长成 \(\sum q(s,t)\times val(s,t)\) 的样子,然后你可以高斯消元打出容斥系数(?)或者有理有据地说明它就是 \((-1)^{|s|+|t|}2^{|s\cap t|}\),总之我们拿到了这个系数。
那么 \(q(s,t)\) 怎么求呢?根据我们的定义可以写出 \(q(s,t)=\prod\limits_{s\subseteq i,t\nsubseteq i}(a_i+1)\prod\limits_{s\nsubseteq i,t\subseteq i}(a_i+1)\prod\limits_{s\subseteq i,t\subseteq i}(2a_i+1)\)。
这个东西长得就很丑,大概做的是一个把 \(s,t\) 中有的元素乘起来,但是两个都有的话就把放一起来乘。
考虑想把 \(s,t\) 独立开,于是变成 \(q(s,t)=\prod\limits_{s\subseteq i}(a_i+1)\prod\limits_{t\subseteq i}(a_i+1)\prod\limits_{s\subseteq i,t\subseteq i}\frac{2a_i+1}{(a_i+1)^2}\)。
这个就很有说法了,首先注意到他们都是能处理出来的,分别记 \(F_s=\prod\limits_{s\subseteq i}(a_i+1),G_s=\prod\limits_{s\subseteq i}\frac{2a_i+1}{(a_i+1)^2}\),那么 \(q(s,t)=F_sF_tG_{s\cup t}\)。
计算答案就是 \(\sum{F_sF_tG_{s\cup t}}(-1)^{|s|+|t|}2^{|s\cap t|}\),我们还想拆 \(s,t\),但发现同时出现了 \(s\cup t,s\cap t\)。
一个经典技巧是 \(|s|+|t|=|s\cup t|+|s\cap t|\),所以我们可以把指数上那个改写,于是我们得到:\(\sum{((-2)^{|S|}F_s)((-2)^{|T|}F_t)(2^{|s\cup t|}G_{s\cup t})}\)。
观察发现这就是个或卷积,做就完了。
QOJ14025
之前就做过一次,但我找不到在哪记录过了。
考虑两个点相遇在 \(u\) 后走到某个点 \(v\) 再做合并操作,实质就是把 \(a_u\) 用 \(dis(u,v)+a_v\) 替换。
而我们可以先把每个点 \(u\) 对应的这种 \(v\) 求出来,只需整个多源 dijkstra 即可。
那么当时 jiangly 讲了一通神秘斯坦纳树,这个东西类似最小生成树,但是允许多加一些点或边。
这个题求的其实就是斯坦纳树,而且如果 \(a_i=0\) 那还是最小生成树。
然后有个比较特别的性质是因为每个点都要被拼起来,而我们能新加的边就是原图的路径,然后可以证出每个点度数不超过二。
然后怎么转一下可以变成用上面处理的信息求最小生成树。
忘了,就先写这么点。
P11832
avenger 这一块。
场上瞪出了子树是连续段,然后发现子树内任意一个数都能先放,然后就能做树。
森林是类似的,可以把某个树的序列整个插入到另一个树上,这个很多方法都能实现。
现在考虑推广到图怎么做。
注意到我们的序列大概是:根和若干个儿子连了边,然后把序列做了分割,每个割开的段都是独立的,不能跟外界有边。
然后你能发现这其实是若干个点双(当然不一定一段是同一个),而且用于分割的点会是割点。
那么如果有一个点双,这就会对我们的处理产生困扰,考虑这个点双怎么弄。
一般提到点双就会让人想到环,考虑这个点双成的环,显然合法的序列只能是顺着环排。
但是这个图十分地复杂,有多个环就不好做了,没法找出最优的。
仔细考虑发现不是这么一回事,题目保证了有解,而当多个环存在时,必定会有某个环中的边盖在了另一个环的一段上,那么就造不出一个合法的方案。
因此我们可以断论:每个点双有且仅有一个极大环。
那么现在只需找出这个极大环就行,不过暴力是 \(\mathcal{O}(n^2)\) 的。
于是引入广义串并联操作:删一度点,缩二度点,叠合重边。
这个东西本来用来找哈密顿回路,显然也能得到我们要的环,具体地:不断做后两个操作把它缩起来,然后再做一次逆过程就可以用链表把这个环串回来。
问题解决了,考虑实现,首先建立圆方树,对圆点可以当树的情况处理,把儿子按照其序列的开头元素排序,对于方点就把对应的点双拉出来跑这个环。
注意点双里的环是定了某个点一定开头或结尾的,所以只要转两下就行,不必写最小表示法。
然后就按着树那样从连通块推广到一般图即可。
ARC203D
考虑相邻两位置:如果初始给 01,那么可以裂成 011,然后分开,左边依然是 01,而右边成了 11,再做一次是 10 和 01,而两者显然是对称的……于是我们怎么做都弄不到 00!
然后假了,因为初始给 00 就行了,但这也是唯一弄出 0 连续段的办法。
而剩下的部分会发现可以随便搓,1 的连续段也是能弄的(011->0111),因此答案大概就是 \(l>1\) 的 0 连续段加上段间非空的间隙个数。
这个东西应该是可以大力线段树的,只需做一些讨论。
不过有一个相对来说更加优美的办法是给 \(i\) 赋权,默认所有位置都有 1 的贡献,发现有三类 \(i\) 需要额外带权:
- \(a_i=1,a_{i+1}=1\),此时 \(i\) 是可以被 \(i+1\) 产生的,\(v_i\gets -1\);
- \(a_{i-1}=0,a_i=0,a_{i+1}=0\),同样可以被省掉,\(v_i\gets -1\);
- \(a_{i-1}=1,a_i=0,a_{i+1}=1\),有两个都是不需要的,\(v_i\gets -2\)。
可以发现不重不漏。
一个 corner case:如果全是 1,那答案必须是 \(n\)。
CF1989F
首先一个位置不会做两次,不然我们就能删除第一次。
行列染色转图论,发现如果一个点染 R,说明行晚于列,可以连一条行到列的边,反之同理。
如果这是 DAG 就直接做了,但是如果有个 scc 那么就需要把里面所有位置同时做来任意决策,不然就会被覆盖。
动态维护 scc 不好做,考虑分治,怎样使得每次一条边只递归一侧呢?
对于 \([l,mid]\) 的边跑 tarjan,然后考虑每条边,如果已经在同一个 scc 里,那么可以直接缩起来不进右侧,否则进左侧。
用并查集维护这个缩点和答案即可。