2024-05-15 18:22阅读: 51评论: 0推荐: 1

数据结构做题记录

I. P3246 [HNOI2016] 序列

莫队

对于当前区间 [l,r],考虑右端点 rr+1 的贡献。不难发现,就是 i=lr+1minj=ir+1{aj}

设区间最小值位置为 pos,则 apos 会贡献 (posl+1) 次,剩下的部分是 i=pos+1r+1j=ir+1min{aj}

发现剩下这部分答案满足可差分性,套路地设 f(r)=i=1rminj=ir{aj},于是这部分就是 f(r+1)f(pos)

思考如何求出 f(i)。设在 i 前面的第一个比 i 大的数的位置是 prei,注意到,f(r)=f(prer)+(rprer)ar,不难递推得到。

对称地,我们对左端点再次讨论即可。利用朴素的 ST 表维护区间最小值,时间复杂度 Θ(nlogn+mn),其中 Θ(nlogn) 是 ST 表预处理时间。

线段树

TODO。

II. P6604 [HNOI2016] 序列 加强版

[l,r] 最小值位置为 pos,它的贡献为 (rpos+1)(posl+1)。然后考虑 [l,pos)(pos,r] 的贡献。

这两部分贡献可以用普通版中莫队的做法中推的式子,具体地说就是考虑端点从 [pos,pos] 推到 [pos,r],除去 pos 的贡献,剩下那部分就是 k=pos+1ri=pos+1kminj=pos+1i{aj},可以利用 f(i) 的二维前缀和表达。对于 [l,pos) 的贡献同理。

综上,我们可以 Θ(1) 回答每个询问。时间复杂度瓶颈在于朴素 ST 表的预处理,为 Θ(nlogn+q)

根据 Alex_wei 老师的说法,如上做法和 Cartesian 树是有关系的。相关题目:P6503 [COCI2010-2011#3] DIFERENCIJA

III. #6376. 「HNOI2016」序列 二次加强版

朴素 ST 表无法通过,只能考虑 Θ(n)Θ(1) RMQ。

参见下一篇题解。

IV. P3793 由乃救爷爷

Θ(n)Θ(1) RMQ。

事实证明确定性的分块+ST 表跑得要比非确定性的 Cartesian 树慢得多()

我的实现中,ST 表+分块跑了 11.17s,而 Cartesian 树跑了 4.50s

算法 1:ST 表+分块(确定性)

参考了 RI 的题解。RI 叫它二毛子()。

考虑将序列分成若干块,每块大小为 B=logn。我们对块间做 ST 表,时间复杂度是 Θ(nBlognB)=Θ(n) 的。

为了处理零散的段,我们预处理块内前缀最大值、块内后缀最大值。额外地,对于两端点在同一段的情况,我们需要额外处理一个 val 数组,利用二进制压位,维护一个递减的子序列。

例如,A={1,5,4,3},那么 val={{1},{2,1},{3,2},{4,3,2}}

对于每次询问,如果两端点在同一块的情况,我们可以直接利用位运算找到最大值。

否则,先处理散块,再 ST 表查询整块。查询时间复杂度 Θ(1)

综上,我们在 Θ(n)Θ(1) 内支持了 RMQ 的查询。

#include <bits/stdc++.h>
using namespace std;
// #define int long long
using ull=unsigned long long;
constexpr int MAXN=2e7+64, B=64;
namespace GenHelper {
unsigned z1,z2,z3,z4,b;
unsigned rand_() { }
}
void srand(unsigned x) { }
int read() { }
int a[MAXN], pre[MAXN], suf[MAXN];
int f[19][MAXN/B+1]; ull val[MAXN];
#define bel(x) ((x+63)>>6)
#define st(x) (((x-1)<<6)|1)
#define ed(x) (x<<6)
int n, m; int stk[MAXN], top;
void build() {
int bnum=bel(n);
for (int i=1; i<=bnum; ++i) {
pre[st(i)]=a[st(i)]; suf[ed(i)]=a[ed(i)];
for (int j=st(i)+1; j<=ed(i); ++j) pre[j]=max(pre[j-1],a[j]);
for (int j=ed(i)-1; j>=st(i); --j) suf[j]=max(suf[j+1],a[j]);
f[0][i]=pre[ed(i)];
}
for (int i=1; i<=__lg(bnum); ++i)
for (int j=1; j+(1<<i)-1<=bnum; ++j)
f[i][j]=max(f[i-1][j],f[i-1][j+(1<<i-1)]);
for (int i=1; i<=bnum; ++i) {
top=0;
for (int j=st(i); j<=ed(i); ++j) {
if (j>st(i)) val[j]=val[j-1];
while (top&&a[j]>=a[stk[top]]) val[j]^=1ull<<stk[top--]-st(i);
val[j]|=1ull<<j-st(i); stk[++top]=j;
}
}
}
int ask(int l, int r) {
int x=bel(l), y=bel(r);
if (x==y) return a[l+__builtin_ctzll(val[r]>>l-st(x))];
int ans=max(suf[l],pre[r]);
if (x+1<y) {
x++, y--;
int t=__lg(y-x+1);
ans=max({ans,f[t][x],f[t][y-(1<<t)+1]});
}
return ans;
}
ull ans=0;
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
unsigned s;
cin>>n>>m>>s; srand(s);
for (int i=1; i<=n; ++i) a[i]=read();
build();
int l, r;
while (m--) {
l=read()%n+1, r=read()%n+1; if (l>r) swap(l,r);
ans+=ask(l,r);
}
cout<<ans;
}

算法 2:Cartesian 树(依赖随机数据)

我们 Θ(n) 建出 Cartesian 树。

我们知道,RMQ 问题可以转为 Cartesian 树上的 LCA 问题。本题中,由于数据随机,所以直接暴力跳 LCA 即可。时间复杂度期望 Θ(1)

V. [AGC028B] Removing Blocks

VI. P5443 [APIO2019] 桥梁

首先,在 i 号灯变量的时刻,我们设包含 i 的极长连通段为 [l,i],包含 i+1 的极长联通段为 [i+1,r],那么 s[l,i],t[i+1,r],都被连通了。发现我们可以对 [l,i],[i+1,r] 做一个矩形加。

VII. P4314 CPU 监控

TODO。

VIII. P6242 【模板】线段树 3

IX. CF997E Good Subsegments

考虑套路地进行扫描线。

X. P5046 [Ynoi2019 模拟赛] Yuno loves sqrt technology I

XI. P5298 [PKUWC2018] Minimax

XII. P5048 [Ynoi2019 模拟赛] Yuno loves sqrt technology III

XIII. P6684 [BalticOI 2020 Day1] 小丑

XIV. P3247 [HNOI2016] 最小公倍数

操作分块+并查集。

具体地,将边按照 a 分块,块内按照 b 排序;

将询问对 b 排序,然后按照 a 挂到块中。

对于整块,由于我们已经按照 b 排序,所以直接处理即可;对于散块,暴力加入后撤销即可。

记块大小为 B,时间复杂度 Θ(qBlogB+Bmlogm)。取 B=mlogm 即可。

XV. P5072 [Ynoi2015] 盼君勿忘

XVI. P2479 [SDOI2010] 捉迷藏

XVII. P5324 [BJOI2019] 删数

XVIII. P4513 小白逛公园

线段树维护区间最大子段和的板子。

XIX. P5327 [ZJOI2019] 语言

TODO。

XX. P5012 水の数列

TODO。

XXI. P5283 [十二省联考 2019] 异或粽子

转为异或前缀和是平凡的。

Solution 1:Trie

不妨将 k 乘以 2,转为无序的问题。

我们维护四元组 (i,k,v),表示与 sumi 异或,第 k 大的值为 vv 不难利用 Trie 上二分(考虑类似平衡树的二分方式)在 Θ(logV) 内找出。

于是,平凡地,我们对于 i[0,n],将 (i,1,v) 插入大根堆中。每次取出最大的值 (i,k,v) 后,将 (i,k+1,v) 压入堆中。

时间复杂度 Θ((n+k)logV),时空常数明显优于解法 2。

Solution 2:持久化 Trie

类比 XXIX 超级钢琴 的套路,维护五元组 (st,l,r,pos,v) 表示 [l,r] 区间内,与 sumst 异或值最大的位置是 pos,最大的值是 v

pos 不难利用持久化 Trie 求出。每次取出 (st,l,r,pos,v) 后,我们将 (st,l,pos1,pos,v)(st,pos+1,r,pos,v) 压入堆中即可。

时间复杂度 Θ((n+k)logV)

XXII. CF125E MST Company

经典结论:设点集内 dfs 序最小和最大的点分别为 u,v,则点集的 LCAlca(u,v)

于是维护区间最大/次大/最小/次小 dfs 序即可。时间复杂度 Θ(mlogn)

XXIII. P5926 [JSOI2009] 面试的考验

这是三倍经验的第一题,我们考虑利用分块在 Θ(n+m)n 的时间复杂度内解决。

XXIV. CF765F Souvenirs

这是三倍经验的第二题,我们考虑利用权值线段树在 Θ(nlog2V) 的时间复杂度内解决。

XXV. CF1793F Rebrending

这是三倍经验的第三题,我们考虑利用根号分治在 Θ(nn+m) 的时间复杂度内解决。

XXVI. UVA1619 感觉不错 Feel Good

XXVII. [ABC311G] One More Grid Task

XXVIII. P2839 [国家集训队] middle

XXIX. P2048 [NOI2010] 超级钢琴

XXX. CF1192B Dynamic Diameter

考虑利用线段树维护 Euler 序。

不妨设 stuu 第一次出现的位置。

引理 1 u,v 的 LCA 是 [stu,stv] 中深度最小的点。

引理 2 边权非负的时候,直径长度即为

maxu,v{depu+depv2deplca(u,v)}

也就是说,我们的答案就是

maxuwv{depu+depv2depw}

这启示我们,在线段树的节点上维护:

  • mx1=max{depu}
  • mn1=min{depu}
  • lmx=maxuv{depu2depv}
  • rmx=maxvu{depu2depv}
  • ans=maxu,v{depu+depv2deplca(u,v)}

考虑合并两个节点 l,r

mx1,mn1 的合并是平凡的。

lmx,rmx 的合并

直接取四种情况的 max 即可,是不难的。

ans 的合并

  • ansl,ansr
  • lmx+mx2
  • rmx+mx1

max 即可。

考虑如何修改。对应区间上,我们有

  • mx1mx1+Δ
  • mn1mn1+Δ
  • lmxlmxΔ
  • rmxrmxΔ

加上 tag 后直接做就可以了。

时间复杂度是 Θ((n+q)logn) 的。

XXXI. P8078 [WC2022] 秃子酋长

莫队的 Θ(nmlogn) 是平凡的。由于 n,m5×105,无法通过。我们考虑 Θ(nm) 的做法。

考虑排列的逆排列。不难发现,时间复杂度的瓶颈在于插入,如果没有插入只有删除的话,可以利用双向链表做到 Θ(1)

那么考虑不插入的回顾莫队

首先建出整个序列对应的双向链表。

  1. 将询问按照左端点所在块编号为第一关键字(升序),右端点为第二关键字降序排序;
  2. 左端点处理到块 [st,ed] 的时候,将 [1,st) 在双向链表中删除;
  3. 记录此时的状态(此时右端点在 n 处),处理询问:
    • 将右端点移动到询问的右端点 R
    • 将左端点移动到询问的左端点 L,回答询问;
    • 撤销左端点的移动,将左端点移回到 st
  4. 处理完 [st,ed] 的询问后,将 3 中记录的状态还原,回到 3 处理下一块。

时间复杂度 Θ(nm)。本题对常数要求较高,需要精细实现。

XXXII. [ABC244Ex] Linear Maximization

对于 b=0 的情况,直接用 std::set 维护即可。

否则,我们利用一棵李超线段树维护。具体地说,注意到

ax+by=b(xab+y)

就是求一些一次函数 y=kx+bk=x,b=y)在点 ab 处的最值。我们利用李超线段树维护这些分数即可。

时间复杂度 Θ(nlogn)

本题还有利用凸包的解法,详见 XXVIII

XXXIII. P3309 [SDOI2014] 向量集

答案显然在凸包上。由于涉及区间询问,所以考虑线段树。

具体地说,我们在线段树的每个节点上维护一个凸包。查询时,在 Θ(logn) 个节点上二分即可,总时间复杂度是 Θ((n+q)log2n) 的,吗?

考虑插入。如果每次插入我们都 Θ(nlogn) 暴力重构凸包,显然时间复杂度不正确。

但是可以在一个区间刚好满的时刻(也就是,加入的元素使得这个区间被填满)建立凸包,这样时间复杂度就正确了。正确性显然。

现在是 18:29,我看看我什么时候写完。

其实还是很好写的。code.

XXXIV. P5631 最小 mex 生成树

我们有两种解法。

整体二分

暴力的做:将边权等于 x 的边删掉,看可不可行。

考虑整体二分,solve(l,r) 表示将边权 [l,r] 的边删掉,可不可行。

时间复杂度 Θ(VlogVlogn)

线段树分治

在边权上建立线段树,将一条边 (u,v,w) 拆成 (u,v,[0,w))(u,v,(w,+))。然后利用可撤销并查集维护即可,时间复杂度 Θ(VlogVlogn)

XXXV. SP9576 Dynamic Graph Connectivity

XXXVI. P3899 [湖南集训] 更为厉害

XXXVII. P5227 [AHOI2013] 连通图

XXXVIII. CF1628E Groceries in Meteor Town

XXXIX. [ABC295G] Minimum Reachable City

XL. P6089 [JSOI2015] 非诚勿扰

XLI. [ABC342G] Retroactive Range Chmax

XLII. P3521 [POI2011] Tree Rotations 旋转树木

XLIII. [ABC341G] Highest Ratio

XLIV. P4093 [HEOI2016/TJOI2016] 序列

XLV. P4848 崂山白花蛇草水

XLVI. P5445 [APIO2019] 路灯

XLVII. P5689 [CSP-S2019 江西] 多叉堆

XLVIII. P10590 磁力块

IL. P10589 楼兰图腾

L. P3332 [ZJOI2013] K大数查询

LI. P2617 Dynamic Rankings

LII. P4175 [CTSC2008] 网络管理

首先套路地整体二分。

LII. P3242 [HNOI2015] 接水果

首先套路地整体二分。

考虑刻画路径 (u,v)(s,t) 包含的条件。设 dfn(u)dfn(v),dfn(s)dfn(t)

  • LCA(u,v)u

那么,只需要 su 子树内,tv 子树内就好了。

即,dfn(s)[dfn(u),dfn(u)+siz(u)1]dfn(t)[dfn(v),dfn(v)+siz(v)1]

  • LCA(u,v)=u

u 的儿子中,v 的祖先为 p

那么,只需要 s 不在 p 子树内,tv 子树内就好了。

即,

  • dfn(s)[1,dfn(p)1]dfn(t)[dfn(v),dfn(v)+siz(v)1]
  • dfn(s)[dfn(v),dfn(v)+siz(v)1]dfn(t)[dfn(p)+siz(p),n]

将路径 (s,t) 转化为二维平面上的点 (dfn(s),dfn(t))。对于每个盘子 (u,v),做矩形加,然后对于每个水果做单点查即可。

最后,扫描线即可。时间复杂度 Θ((p+q)log2n)

LIII. P5344 【XR-1】逛森林

LIV. P5025 [SNOI2017] 炸弹

LV. [Nikkei 2019 Final F] Flights

题解

LVI. P5471 [NOI2019] 弹跳

LVII. P3709 大爷的字符串题

直接回滚莫队即可,时间复杂度 Θ(mn)

LVIII. P1997 faebdc 的烦恼

LIX. P10638 BZOJ4355 Play with sequence

第二个操作可以看成是区间加与区间 chmax 的复合。于是只需要维护区间覆盖、区间加、区间 chmax 这三个操作就好了。

对于区间 chmax,我们用 segbeats 的套路,具体地说:

维护区间次小值 sec 和区间最小值 mn,设 chmax 的值是 v。当当前区间完全包含询问区间时:

  • vmn:无事可做,直接返回;
  • mn<v<sec:直接修改 mn 即可;
  • secv:递归处理。

吉如一老师证明了这么做的时间复杂度是 Θ(mlog2n) 的。

区间加和区间 chmax 是平凡的。只需要再维护一个区间最小值的数量 cnt,查询的时候就是查询 cnt[mn=0]

需要注意 tag 下传的顺序:先下传区间覆盖,再下传区间加,最后区间 chmax。区间覆盖之后要把区间加的 tag 清除掉。

似乎还有一种做法就是设 tag (a,b)xmax(x+a,b),然后注意到这个东西是满足结合律的。

代码

LX. CF526F Pudding Monsters

LXI. CF997E Good Subsegments

LXII. P4462 [CQOI2018] 异或序列 / IL. CF617E XOR and Favorite Number

/fn

直接莫队即可。时间复杂度 Θ(mn)

LXIII. P9631 [ICPC2020 Nanjing R] Just Another Game of Stones

第一问可以直接上吉司机线段树。

LXIV. P10639 BZOJ4695 最佳女选手

Segbeats 板子题。我们维护区间的

  • mx,secmx,mxcnt:最大值,严格次大值,最大值的个数;
  • mn,secmn,mncnt:最小值,严格次小值,最小值的个数;
  • sum:区间和。

以区间 chmax v 为例,当当前区间完全包含询问区间时:

  • vmn:无事可做,直接返回;
  • mn<v<secmn:直接修改 mn 即可;
  • secmnv:递归处理。

chmin 同理,对于 add 操作只需要维护一个 lazytag 即可。

需要注意的是,本题中同时有 chmax 和 chmin 操作,对于区间内只有 1~2 个本质不同的数的时候,需要特别处理一下。

同时需要注意 pushdown 时各个 tag 的顺序。

然后就做完了,代码很好写。时间复杂度可以证明是 Θ(mlog2n) 的。

LXV. P5354 [Ynoi2017] 由乃的 OJ

首先,Θ(mlog2nlogV) 的做法是显然的,但是过不去。但是你发现可以用 ull Θ(1) 信息合并,就做完了。

LXVI. P10637 BZOJ4262 Sum

TODO。

LXV. [AGC014E] Blue and Red Tree

LXVI. P3604 美好的每一天

套路地状态压缩,求一遍异或前缀和,问题转化为:

求满足 l1i<jr,且 aiaj=2k=0(i,j) 数量。

显然可以用莫队。时间复杂度 Θ(mnlogV)

LXVII. [ARC122D] XOR Game

LXVIII. P2468 [SDOI2010] 粟粟的书架

经典二合一。

对于前 50% 的数据,考虑整体二分,然后用二维 BIT 维护,这样是 Θ(mlogVlog2n) 的。

对于剩下的数据,考虑持久化线段树。这样是 Θ(mlognlogV) 的。

LVIII. P4559 [JSOI2018] 列队

经典结论是,将学生升序排序后,依次匹配 K,K+1,,K+rl 就是最优答案。

考虑怎么求答案。绝对值看起来很难受,我们不妨分成几段考虑。

对于 [1,K) 这个区间,答案就是 cnt(cnt+1)2+Kcntsum;对于 (K+rl,N] 这个区间,答案就是上式的相反数,这两段是好求的。

接下来就是 [K,K+rl] 这段区间内的贡献了。二分出从哪开始,学生休息的位置超过列队的位置,然后算贡献即可。

时间复杂度 Θ(mlogV)

LXIX. P3992 [BJOI2017] 开车

本文作者:Starrykiller

本文链接:https://www.cnblogs.com/Starrykiller/p/18194470/ds-practise

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Starrykiller  阅读(51)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.