算法竞赛进阶指南-选做

Posted on 2024-03-02 16:48  _XOFqwq  阅读(8)  评论(0编辑  收藏  举报

算法竞赛进阶指南-学习笔记 之后的新内容。

将会比前面的简略许多,因为仅供个人使用。

0x41 并查集

P1955

相等关系合并端点,不等关系若端点在同一集合则矛盾。注意离散化。实现

UVA1316

solution

P1196

solution

P5937

首先容易发现,若令 \(s_i\)\(01\) 序列前缀和,则 \(s_r\)\(s_{l-1}\) 奇偶性相同则有偶数个 \(1\),不同则有奇数个 \(1\)

边带权做法:

令边权为 \(d_x\),若为 \(0\)\(x\)\(fa_x\) 奇偶性不同,反之则相同。

我们对于每个 \(x\),把从 \(x\)\(fa_x\)\(d\)\(\operatorname{xor}\) 和即为 \(x\)\(fa_x\) 的奇偶性关系。

对于询问 \((x,y)\),若它们在同一集合,则若 \(d_x \operatorname{xor} d_y=ans\)\(ans\) 为答案)则不矛盾,否则矛盾;

若它们不在同一集合,则 \(x\)\(y\) 的奇偶性关系 \(ans\) 即为 \(d_x \operatorname{xor} d_p \operatorname{xor} d_y\),进而可得两集合新边权 \(d_p=d_x \operatorname{xor} d_y \operatorname{xor} ans\)

边带权做法的 实现

扩展域做法:

将每个点 \(x\) 拆成 \(x_{odd}\)\(x_{even}\)

\(ans=0\),则若 \(x_{odd}\)\(x_{even}\) 在一个集合中则矛盾,否则合并 \(x_{odd},y_{odd}\)\(x_{even},y_{even}\)

否则反之同理可得。

扩展域做法的 实现

还是注意离散化。

0x42 树状数组

Acwing241

对于每个 \(y_i\),它对于 ^ 的贡献即为 左边比 \(y_i\) 小的 \(y_j\) 个数 \(\times\) 右边比 \(y_i\) 小的 \(y_k\) 的个数。

同理亦可算出其对于 v 的贡献,分别用树状数组维护即可。

实现

Acwing242

差分即可。实现

P3372

仍然考虑差分。令差分数组为 \(b\)

此时若对 \(a_{1 \sim x}\) 进行区间增加 \(v\),则其总增量即为

\[\sum_{i=1}^x \sum_{j=1}^i b_j \]

考虑拆这个式子:

\[\sum_{i=1}^x \sum_{j=1}^i b_j \]

\[= \sum_{i=1}^x (x-i+1) \times b_i \]

\[= (x+1) \sum_{i=1}^x b_i - \sum_{i=1}^x i \times b_i \]

于是我们维护两个数组 \(c_0,c_1\),分别保存 \(\sum_{i=1}^x b_i\)\(\sum_{i=1}^x i \times b_i\)

然后按照上式进行区间修改与查询即可。

实现

Acwing244

倒序确定每头牛,容易发现每头牛的身高 \(H_k= 1 \sim n\) 中第 \(A_k+1\) 小的不存在于 \(H_{k+1 \sim n}\) 的数。

于是我们用树状数组维护 \(01\) 序列 \(b\) 的前缀和,\(b\) 起初全为 \(1\)

二分这个位置,通过 ask(mid) 即可查询前面有多少个 \(1\),若比 \(A_k\) 小则往大猜,否则往小猜。

二分做法的 实现

当然用倍增也可以做,就是枚举 \(p\),以 \(2^p\) 为步长去凑即可。这个实现先咕着。

0x43 线段树

SP1716(Acwing245)

很基础的线段树,但是实现细节挺多。

\(sum\) 维护区间和,\(lmax\) 维护靠左端的最大子段和,\(rmax\) 维护靠右端的区间子段和,\(dat\) 维护区间子段和。

于是 pushup 时有:

(以下令 \(p\) 为父节点,\(ls,rs\) 为左、右子节点)

\[\large{t_{p_{sum}}=t_{ls_{sum}}+t_{rs_{sum}}} \]

\[\large{t_{p_{lmax}}=\max(t_{ls_{lmax}},t_{ls_{sum}}+t_{rs_{lmax}})} \]

\[\large{t_{p_{rmax}}=\max(t_{rs_{rmax}},t_{rs_{sum}}+t_{ls_{rmax}})} \]

\[\large{t_{p_{dat}}=\max(t_{ls_{dat}},t_{rs_{dat}},t_{ls_{rmax}}+t_{rs_{lmax}})} \]

buildupdate 中如此传递信息即可。

ask 中,我们也是如此累计答案,但在 \(l > mid\)\(r \le mid\) 的情况时(\([l,r]\) 为询问区间,\(mid\) 为当前区间中点),答案的 \(lmax\)\(rmax\) 需要分别对于 \(\large{t_{rs_{lmax}}}\)\(\large{t_{ls_{rmax}}}\)\(\max\)

实现

Acwing246

直接用线段树维护带修的区间 \(\gcd\) 是过不了的,

所以我们建立原数组 \(A\) 的差分数组 \(B\)

这样就把区间修改转为两次单点修改。

因为我们有:

\[\gcd(a_1,a_2,...,a_n)=\gcd(a_1,a_2-a_1,...,a_n-a_{n-1}) \]

(这玩意似乎可以用 \(\gcd(x,y)=\gcd(x,y-x)\)(更相减损术)运用数学归纳法证明,但是我先咕着)

所以可以运用线段树维护 \(B\) 的区间 \(\gcd\)

然后询问就等于求出 \(\gcd(a_l,\operatorname{ask}(1,l+1,r))\)

其中因为要求 \(a_l\),所以还得套个树状数组。

然后这题就做完了。实现

P3372

板子。实现