2023.9 ~ Here I feel the touch brought by the real number solution

LOJ2785

考虑从前往后推,考虑 \(p_i,p_{2i},p_{2i+1}\),我们发现在这里进行完交换之后左右子树就独立了;而我们的首要目标是最小化 \(p_i\)。设这三个数是 \(a,b,c\),讨论一下两边是否交换,发现交换完之后有四种情况:

    a    |    b    |    c    |    c
  b   c  |  a   c  |  b   a  |  a   b

可以发现,如果 \(a,b\) 之一是最小值,那么交换方案唯一;否则我们需要把 \(c\) 换上来,然后决策把 \(a,b\) 中的哪个分配到左边,哪个分配到右边。

不难发现这只需要知道 \(a,b\) 如果放在左边或右边,它们最终会被排到什么位置;然后我们尽量把小的那个排的靠前即可。可以设 \(f(u,j)\) 表示如果在 \(u\) 的子树内,先令 \(p_u=j\),最终这个 \(j\) 会被放到什么位置,但时间复杂度至少为 \(O(n^2)\)

注意到所有点的子树大小之和为 \(O(n\log n)\),而如果 \(u\) 的子树内有 \(x\) 个点,所有的 \(f(u,\cdot)\) 至多只会有本质不同的 \(x+1\) 种。因此直接记录就行了,复杂度不超过 \(O(n\log^2n)\)

LOJ2472

考虑依次给 \(i=1,2,\cdots,n\) 填上数,每次尽量填最大的。考虑什么时候 \(i\) 填上 \(x\) 是合法的。考虑 Hall 定理,发现左部点约束最严的时候肯定是找一个已经填过的点 \(u\),然后对所有 \(d_v\ge d_u\)\(v\),选出 \(v\) 的子树内的所有点,也就是说我们会选一个后缀的子树的并。形式化地,对每个已经填过的点 \(u\),所有 \(d_v\ge d_u\)\(v\) 的子树内未填的点并起来的大小不能超过 \(\ge d_u\) 且尚未被填入的格子个数。

维护 \(w_i\) 表示如果在判定中选择分界点为 \(i\)\(\ge i\) 的剩余空位数减去所有 \(d_u\ge i\) 的子树并中未填点数的差,那么当前状态合法当且仅当 \(w_i\ge 0\) 对所有 \(i\) 成立。

考虑给 \(u\) 填入 \(x\) 有什么影响,发现对前一项的影响是所有 \(i\le x\)\(w_i\leftarrow w_i-1\)。考虑后一项会发生什么变化,我们发现由于树的 BFS 序就是 \(1,2,\cdots,n\),因此 \(u\) 的子树内一定完全是空的,祖先一定是满的。因此,设 \(v\)\(u\) 的父亲,显然只有 \(i\le x\)\(w_i\) 会发生改变,具体地:

  • \(i \le d_v\) 时这个子树并中未填的点的个数会 \(-1\),导致实际的 \(w_i\) 不改变;
  • \(d_v<i\le x\) 时,子树并会多出 \(\text{size}_u-1\),导致实际的 \(w_i\) 的变化为 \(w_i\leftarrow w_i-\text{size}_u\)

因此,实际的影响就是:对所有 \(d_v<i\le d_u\),令 \(w_i\leftarrow w_i-\text{size}_u\)。那么要想维持 \(w_i\ge 0\),就只需要找到 \(d_v\) 后面的最大的 \(x\),使得对 \(i\in (d_v,x]\),均有 \(w_i\ge \text{size}_u\),然后将这个区间都减去 \(\text{size}_u\)。线段树维护即可,时间复杂度 \(O(n\log n)\)

LOJ2473

考虑对每个 \(x\) 算出第 \(k\)\(\ge x\) 的方案数,这个就是对每个点 \(i\) 如果 \(d_i\ge x\) 就把他的权值设为 \(1\), 否则权值为 \(0\),那么第 \(k\)\(\ge x\) 相当于是说这个连通块里至少有 \(k\)\(1\)

直接 DP,复杂度是 \(O(n^3)\)\(n=1666\),而且还开 5s,这不是稳过?过了,好像 2s 都不到。。

我去。。怎么正解是 \(O(n^2\log n)\),有点猛了

简单写一下题解做法:考虑设 \(f_{u,i,j}\) 表示选一个以 \(u\) 为根的连通块,使得其中 \(\ge i\) 的点有恰好 \(j\) 个的方案数。那么答案就是 \(\sum_{u,i}\sum_{j=k}^{n}f_{u,i,j}\)

\(F_{u,i}(x)\) 为其生成函数,我们考虑 DP 是咋做的,发现会先把所有 \(p\ge a_u\)\(f_{u,p,1}\leftarrow 1\),所有的 \(p<a_u\)\(f_{u,p,0}\leftarrow 1\)。然后接下来枚举儿子 \(v\),让所有 \(F_{u,p}\leftarrow F_{u,p}\times(F_{v,p}+1)\)

直接维护多项式当然很寄,考虑插值:取点值 \(t\),发现我们的操作对 \(F_{u,i}(t)\) 的影响是:

  • 令一个区间内的 \(f_{u,p,1}\leftarrow 1\):令所有的 \(F_{u,p}(t)\leftarrow t\)
  • 令区间内的 \(f_{u,p,0}\leftarrow 1\)\(F_{u,p}(t)\leftarrow 1\)
  • \(F_{u,p}\leftarrow F_{u,p}\times(F_{v,p}+1)\):先在对面全局 \(+1\),然后对应点值相乘。

线段树合并维护即可。只需要维护区间加,区间乘。然后我们考虑最终直接对着所有 \(F_{u,i}\) 之和去插值(也就是对这个总和多项式插值),那也就是要再多维护一个区间和。

做一次的复杂度是 \(O(n\log n)\),插值做 \(n\) 次就是 \(O(n^2\log n)\)。最后插回来系数就行了。

LOJ2477

考虑 \(C=1\) 怎么做,发现这个时候每个人的选择是唯一的,我们可以直接 \(O(nm)\) 算一轮,对于第二问就直接二分,复杂度是 \(O(n^2m\log n)\)

那么 \(C>1\) 怎么办呢,这个时候问题在于某些人可能有多种选择,而他的多种选择可能带来不同的结果。一种想法是类似二分图匹配的匈牙利算法,我们对每个人从大往小枚举他可能的候选导师,如果当前这个导师已经满员了,我们尝试找这个导师的一个学生,让他再去换一个别的导师。

考虑仿照匈牙利算法,依次考虑每个人 \(x\),发现他能匹配上导师 \(y\) 当且仅当 \(y\) 能够通过当前的残量网络到达一个没有满的导师。我们找到符合条件的 \(y\) 中志愿最靠前的一个即可。

我们在每轮增广后 BFS 算出每个点能否到一个没有匹配满的导师即可。那么这部分的时间复杂度还是 \(O(nm)\)。进一步你发现二分都不需要了,我们在尝试加入 \(i\) 的时候直接顺便把别的都算了就行。复杂度是 \(O(n^2m)\)

LOJ2478

相当于选 \(k+1\) 条点不相交的路径,最大化点权和。

wqs 二分,然后 DP 的时候记录 \(f_u\) 表示 \(u\) 点的答案,\(g_u\) 表示考虑 \(u\) 点的子树,要求存在一个以 \(u\) 为端点的链时候的答案,那么转移就是:

  • \(f_u\) 有三种情况,第一种是直接所有儿子的 \(f_v\) 加起来,第二种是选择一个儿子 \(x\),用其他儿子的 \(f\) 之和加上 \(g_x+w(x,u)\) 来更新答案,第三种是合并两条链,这种情况还需要加上一个二分的斜率 \(m\),因为链的总数少了一条。
  • \(g_u\) 可以直接选自己这个单点作为转移点,这种情况下是所有儿子的 \(f\) 之和减去 \(m\),另一种方法是选一个儿子 \(x\) 然后用 \(g_x+w(x,u)\) 加上其他儿子的 \(f\) 之和更新答案。

然后为了要 wqs 还得记录选了多少条,那就记录 \(\textit{cf}_u,cg_u\) 分别表示最优情况下选的链的个数,如果有多解选尽可能多条,最终会选多少。这也是容易转移的。

总复杂度 \(O(n\log V)\)。本来想先写个暴力 \(O(\sum \deg^2)\) 转移试试,结果直接过了,惊讶

感觉这题放现在可能也就是个 d1t2。。比 coat iiidx mentor 都要简单

LOJ2479

用总的减去不合法的。考虑不合法的怎么算。

考虑在 SAM 上定位到询问串 \(t=S[l\cdots r]\),记 \(L=r-l+1\),相当于要切两刀割开所有字符串。

找到 endpos 集合中的最大值 \(m\),那么后一刀切的位置 \(p\) 需要满足 \(p\in[m-L+1,m-1]\)。这里在 \(p\) 处切一刀表示分开 \(p,p+1\) 两个位置,即取 \(i=p\)\(j=p+1\)

如果在靠后的那一刀的位置是 \(p\),那么接下来我们应当找到 \(\le p\) 的 endpos 中的最大值 \(y\) 和最小值 \(x\),然后把答案加上 \(|[x-L+1,x-1]\cap[y-L+1,y-1]|\)。如果所有 endpos 都 \(>p\) 那下一刀可以随便切,方案数是 \(p-1\)。这种情况能发生当且仅当所有 endpos 的交非空。

形式化地我们设 \(f(p)\) 表示 \(\le p\) 的最大的 endpos 位置,\(nx(p)\) 表示 \(>p\) 的最小 endpos 位置,那么 \(f(p)=y\) 当且仅当 \(nx(y)>p\ge y\),那么答案就是

\[\sum_{p=m-L+1}^{m-1}\max(x-1-f(p)+L,0)\\ \]

交换求和顺序,枚举 \(f(p)\) 得到

\[\sum_{y\in\text{endpos}(t)}|[x-L+1,x-1]\cap[y-L+1,y-1]|\times|[y,nx(y)-1]\cap[m-L+1,m-1]| \]

我们仔细地写一下限制,前一条是说 \(y-L+1\le x-1\),后一条需要 \(m-L+1\le nx(y)-1\),两条都满足之后方案数就是 \((x-1-y+L)\times (nx(y)-1-\max(m-L,y-1))\)。这里当 \(y\) 是最大值的时候我们认为 \(nx(y)=n+1\);同时还需要特判一下所有 endpos 都有交的情况,大概是一个二次函数。

首先由 \(y-L+1\le x-1\) 得到的关于 \(y\) 的限制是一个前缀,由 \(m-L+1\le nx(y)-1\) 得到的限制是一个后缀,交一下就是一个区间 \([l,r]\)。然后大概还需要对 \(y\le m-L+1\)\(y>m-L+1\) 分类讨论。

先考虑 \(y\le m-L+1\),我们拆出 \(1,y,nx(y),y\times nx(y)\) 相关的四项,答案就是

\[(x+L-1)(L-m-1)\times\sum 1\\+(m-L+1)\times\sum y\\+(x+L-1)\times\sum nx(y)\\ -\sum nx(y)\times y \]

然后再考虑 \(y>m-L+1\)。这个时候答案是 \((x-1-y+L)\times(nx(y)-y)\),拆一下就是

\[-(x+L-1)\times \sum y\\ +(x+L-1)\times\sum nx(y)\\ +\sum y^2\\ -\sum y\times nx(y) \]

那就是要维护区间内的 \(\sum y,\sum nx(y),\sum nx(y)\times y,\sum 1\)。先离线然后在 parent 树上跑线段树合并就行了,线段树需要支持查询区间个数,区间和,区间邻项乘积和,区间平方和,以及 lowerbound。

然后为了定位到这个节点,大概还需要在 parent 树上倍增。。

具体来说我们线段树维护区间内 endpos 的个数以及 endpos 位置的和,然后还要实现一个 lowerbound,那么要查询区间 \([l,r]\) 内的 \(\sum nx(y)\) 就先找到 \(\ge l\) 的第一个 endpos 中的位置 \(p\),再找到 \(>p\) 的第一个位置;在 \(r\) 那边我们也这么做一遍,然后再查询这个新区间内的 \(\sum y\)

\(\sum y\times nx(y)\) 也是类似的。考虑线段树合并咋维护 \(\sum y\times nx(y)\),发现只需要多维护 \(L(p),R(p)\) 表示区间内最左端和最右端的 endpos 位置就行了。定位这个节点相当于在 parent 树上找一个最深的祖先 \(a\) 使得 \(\text{maxlen}_a\ge L\),那就直接倍增就行了。总的复杂度是 \(O((n+q)\log n)\)

写吐了

LOJ3899

扫描线,线段树维护历史和。

具体来说我们维护三个序列 \(S_i,A_i,B_i\),操作有三种:

  • 区间 \(A,B\) 覆盖,或者区间 \(S_i\leftarrow S_i+A_i\times B_i\)

那就维护向量

\[\begin{bmatrix}\sum A_i&\sum B_i&\sum A_i\times B_i&\sum S_i&\sum 1\end{bmatrix} \]

那么区间把 \(A\) 覆盖成 \(v\) 就是右乘上矩阵

\[\begin{bmatrix}0&0&0&0&0\\0&1&v&0&0\\0&0&0&0&0\\0&0&0&1&0\\v&0&0&0&1\end{bmatrix} \]

区间覆盖 \(B\) 同理。区间 \(S_i\leftarrow S_i+A_i\times B_i\) 就是

\[\begin{bmatrix}1&0&0&0&0\\0&1&0&0&0\\0&0&1&1&0\\0&0&0&1&0\\0&0&0&0&1\end{bmatrix} \]

时间复杂度 \(O((n+q)\log n)\)

暴力展开矩阵发现项数不多,只保留非零项可以大幅度减小常数。

LOJ3566

要算的是 \(n\sum a_i^2-(\sum a_i)^2\)

首先大家都知道这个就是交换差分序列相邻两项了

然后大家都知道最优情况一定是差分序列单谷啊啊啊

单谷之后考虑 DP,把 \(d_i=a_i-a_{i-1}\) 降序排序,然后依次考虑每个 \(d_i\),考虑他加到左边和右边会有啥贡献。我们发现这个 \(d_i\) 要么放在左侧的最后一个,要么放在右侧的第一个,而不管放在哪里,我们总可以直接计算出 \(a_i\) 的实际值。

具体来说,如果他放在了左侧,设左侧当前 \(d\) 的和是 \(x\),那么 \(a_i=x+d_i\);如果放在右侧,右侧的值应当是当前的总和 \(S\)(定值)减去左侧的和 \(x\),因此 \(a_i\) 的实际值是 \(a_n-(S-x)\)

那我们发现只要记录 \(\sum d_i\) 就可以直接算出来第一项了,那第二项咋算捏,正常是记录 \(\sum a_i\),但是这样复杂度会变成 \(O(n\times a\times na)=O(n^2a^2)\)。呃呃好像过不去,顶多优化到 \(O(na^3)\)

改成从小到大排序,那么每次会在最左侧或者最右侧加一个,那这样当前的和可以直接确定了,就只需要记录 \(\sum a_i\),可以做到 \(O(n^2a)\)

具体来说设 \(f(m,s)\) 表示前 \(m\) 个数当前的 \(\sum a_i\)\(s\) 的情况下,最小的 \(\sum a_i^2\)。考虑新加入一个 \(d\),如果放在开头那么新的 \(a_1\)\(d\),其余的所有 \(a_i\) 都会变成 \(a_i+d\),那么这样一来原本的 \(\sum a_i^2\) 会变成

\[d^2+\sum (a_i+d)^2=\sum a_i^2+2d\sum a_i+(m+1)\times d^2 \]

于是转移就是

\[f(m,s)\to f(m+1,s+d_{m+1}\times (m+1))+2d_{m+1}\times s+(m+1)\times d_{m+1}^2 \]

如果放在末尾那么新的 \(a_{m+1}=\sum d_i\),有

\[f(m,s)\to f\left(m,s+\sum d_i\right)+\left(\sum d_i\right)^2 \]

最后答案就是 \(\min_i n\times f(n,i)-i^2\)

考虑复杂度,发现 \(\sum a_i=O(na)\),于是总复杂度看上去是 \(O(n^2a)\);但注意到去掉 \(=0\)\(d_i\) 之后剩下的 \(d\) 只有 \(O(\min(n,a))\) 个,而 \(=0\)\(d_i\) 在一开始就会被转移,不会对 \(f\) 造成任何影响,因此实际的复杂度是 \(O(\min(n,a)\times na)\),可以通过。

LOJ3567

首先发现放棋子类似于删边,我们考虑倒过来做,不断地删掉棋子并且合并连通块。

先不考虑吃子,把一个棋子看作直接删掉这个点,然后先考虑连通块内的点数怎么算:先只考虑三类边,发现维护一个带 size 的并查集就行了。

接下来考虑二类边,发现需要查询一行的某个区间或者一列的某个区间内有多少个点没法被三类点走到,首先考虑怎么求出这个区间,发现只需要对每行或者每列维护极长连续段,具体来说其实还是维护并查集,不过只需要维护一下所在行列编号的最大最小值;然后考虑怎么算区间中有多少个点,那就给三类边的并查集加上一个线段树就行了。

最后考虑一类边,发现只需要看一下旁边四个点算过没,没算过就加上去,可以直接维护。

然后考虑吃子,发现三种边都可能走到棋子,仍然是按照 3->2->1 的顺序考虑,先考虑三类边吃子咋办,那就直接维护连通块旁边的棋子的线段树,查询的时候需要查询异色线段树中的一个前缀和;然后考虑二类边吃子,发现顶多吃四个,那也是好维护的;一类边同理。

于是就做完了,复杂度 \(O((nm+q)\log (nm))\)

仔细想一想我们需要干啥:

  • 维护三个并查集,其中两个是查询同行列极长连续段的,另一个是查询某个点所在线段树的根节点编号的。并查集需要维护集合 size,max,min。
  • 维护三个线段树,分别表示 3 类边连通块的线段树和某种颜色的棋子所在的连通块。

代码待补。逮捕逮捕!!!!!

不知道哪来的题

\(n\) 条直线 \(f_i(x)=k_ix+b_i\),有 \(q\) 次操作,每次单点修改一条直线,或者给出 \(x,l,r\),查询

\[\max_{l\le i\le r}f_i(x) \]

的值。\(1\le n,q\le 2\times 10^5\),2s。

考虑没有修改怎么做,发现可以先把询问点排序,然后线段树维护编号维度,一个节点维护这个节点内直线在每个点处取 max 的分段函数,查询的时候可以拆成 \(O(\log n)\) 段区间,由于询问点有序可以直接维护一个指针扫,这样就 1log 解决了无修的情况。

对于有修改的情况,首先算出每条直线的存在区间,然后对时间维度线段树分治,把一条直线拆成 \(O(\log n)\) 段区间,然后在每个线段树节点上考虑这个节点上的直线对该区间内询问的贡献,这部分就是无修情形的做法,那么总复杂度就是 \(O(n\log^2n)\)

貌似通过给修改的直线也预排序,然后线性构建凸包,就可以 1log 了?

AGC013D

考虑 \(O(n^2m)\) 的 DP:\(f(i,l,r)\) 表示已经走了 \(i\) 步,当前黑球个数的可能区间为 \([l,r]\) 的方案数。

转移时,如果 \(l<n\) 就可以往后加一个 01 或者 00\(r>0\) 就可以往后加 10 或者 11,然后得到一个新的约束区间 \(l',r'\),就转移到 \(f(i+1,l',r')\)。具体来说可以写成:

初值为 \(f(0,0,n)=1\),答案是所有的 \(f(m,l,r)\) 之和。

额然后不会了啊

考虑直接对平面上的折线计数但是会算重,但是我们钦定它必须顶到下边界就行了,挺高妙的

值得一提的是从上面的 \(O(n^2m)\) DP 似乎也可以导出这个做法,大概是把状态中的 \(r-l\) 一维写出来然后进行一些推式子

LOJ3388

考虑每次把一个颜色归位。

那怎么做呢,发现我们可以用至多 \(3m\) 次操作把一列的 \(1\) 全都放在上面,\(0\) 全都放在下面。具体来说就是找到别的一列然后让出来一些空位然后就行了。

那完成了这个是不是就很简单,只需要 \(2m\) 次操作就可以归位了。这样第一步的操作数是 \(3nm\),第二步是 \(2m\),一共有 \(n\) 种颜色,总共需要 \((3n+2)nm\) 次操作。

发现超了几倍。。哦但是你发现在做第 \(i\) 次的时候只需要动 \(n-i+1\) 列,并且第一步实际上总的步数好像应该是 \((2n+2)m\),所以是不是自带一些小常数啊。写一下试试。。

我去被卡了。。只有 \(70\) 呃呃

看题解。分治是什么东西啊??好像是说,把前 \(\lfloor n/2\rfloor\) 种看成一种颜色,剩下的看成另一种,然后递归。考虑把一种颜色归位,那就和刚才一样做就行了,非常厉害啊,就是 \(O(nm\log n)\) 了,仔细分析应该有 \(2\) 的常数。非常牛牛牛!!

仔细编一下,我们先用上面的操作把左右分别 shift,左边把右侧的数放到顶上,右边把左侧的数放到顶上,然后每次取出两个柱子,然后把较多的那个顶端的异侧颜色扔到 \(n+1\) 上,然后把较少的那个顶端颜色扔回来,再把 \(n+1\) 先填满较少的那个,再去填较多的那个。然后就还原了一个柱子,总操作数好像是 \(2nm\)。然后就过了,很稳111

LOJ3389

考虑一维咋做,发现相当于找到移动的前缀最小最大值 \(p,q\),以及总的移动距离 \(d\)

那么初始在第 \(i\) 个点开始的步数大概只和他一直 \(+d\)\(> w-q\) 或者 \(\le p\) 的这个位置有关。显然 \(p,q\)\(O(n)\) 的,我们大概就可以这样算一下呃呃呃。

那多维咋办呢,发现相当于第 \(i\)\(+d_i\),然后某一维第一次加到 \(>w_i-q_i\) 或者 \(<p_i\) 是最严的约束,那么步数就只被这一维决定;可能有两维同时达到这个限制那就是由较先顶到头的那个约束到。

考虑设 \(f_i(x)\) 表示第 \(i\) 维如果初始在 \(x\) 会走多少步,那么对于一个 \((x_1,\cdots,x_k)\),他走的步数就应该是所有 \(f_i(x_i)\) 中的最小值。那 \(w_i\le 10^6\) 似乎已经可以做了:直接暴力算出所有 \(f_i(x_i)\),然后总体排序之后算一些简单的组合计数,复杂度大概是把 \(\sum w_i\) 个数排序的复杂度。

很猛啊,写了一下发现直接冲了 \(80\)

posted @ 2023-09-14 07:53  云浅知处  阅读(70)  评论(0编辑  收藏  举报