DS记录

CF377D Developing Game

\(n\) 个工人,第 \(i\) 个工人的能力是 \(v_i\) ,且他只会和能力在 \(l_i\sim r_i\) 间的工人合作。问最多能选出多少人一起合作并输出方案。

\(n\le 10^5,1\le l_i\le v_i\le r_i\le 3\times 10^5\)

view solution

首先考虑使用数学语言描述该问题。对于集合 \(S\) ,它合法当且仅当如下条件同时满足:

\[\max_{t\in S}l_t\le \min_{t\in S}v_t\\ \max_{t\in S}v_t\le \min_{t\in S}r_t \]

容易发现两个限制是相似的,因此只需要考虑其中一个条件,然后将一维问题拓展成二维问题。

仅考虑第一个条件时,可以发现其等价于有若干条线段,第 \(i\) 条线段为 \([l_i,v_i]\) ,要满足集合内的线段有交。拓展到二维就是若干个矩形有交,于是直接扫描线+线段树即可解决。

对于输出方案则随便记录一个最优情况下矩形交内的点,最后扫一遍整个序列即可。

CF453E Little Pony and Lord Tirek

\(n\) 只小马,第 \(i\) 只初始时有 \(s_i\) 的能量,每经过一单位时间能量会增加 \(r_i\) ,能量上限是 \(m_i\) 。接下来有 \(q\) 次操作,每次操作给出 \(t,l,r\) ,表示在时刻 \(t\) 吸收编号在 \(l\sim r\) 的小马的所有能量,即将它们的能量清空,问吸收了多少能量。

\(n,q\le 10^5,0\le s_i\le m_i\le 10^5,0\le r_i\le 10^5\) ,保证询问的 \(t\) 单调不减。

view solution

先考虑一个严格弱化版本:初始的 \(s\) 全为0,且每次都是操作所有小马。假设现在距离上次操作过了 \(t\) 单位的时间。

容易发现我们应该将小马分为两类分别统计贡献。第一类是还未加到上限 \(m\) 的,这要求 \(t\le \lfloor\dfrac {m_i}{r_i}\rfloor\) ,贡献为满足该条件的 \(r_i\) 之和乘上 \(t\) ;第二类是已经加到上限的,贡献为满足 \(t>\lfloor\dfrac {m_i}{r_i}\rfloor\)\(i\)\(m_i\) 之和。

于是考虑将序列按照 \(\lfloor\dfrac {m_i}{r_i}\rfloor\) 从小到大排序,显然存在分界点满足前面是第一类,后面是第二类。每次二分找到该位置即可。

但是现在每次操作的是一个区间,因此要区间赋值“上次修改时间”。可以使用珂朵莉树进行维护。对于上次修改时间相同的一个区间,我们需要快速求出该区间内满足 \(\lfloor\dfrac {m_i}{r_i}\rfloor\ge t\)\(i\)\(r_i\) 之和与满足 \(\lfloor\dfrac {m_i}{r_i}\rfloor<t\)\(i\)\(m_i\) 之和。直接使用主席树进行维护即可。

\(s\) 非零时,只需要对它第一次被吸完能量时特判即可。

复杂度为 \(\mathcal O((n+q)\log n)\)

CF1034D Intervals of Intervals

\(n\) 个区间 \([ai,bi]\) ,定义区间的区间 \([l,r]\) 的价值是第 \(l\) 个区间到第 \(r\) 个区间的并的长度,找出 \(k\) 个不同的区间的区间,使得总价值最大。

\(n\le 3\times 10^5,k\le \min(10^9,\dfrac{n(n+1)}2),1\le a_i<b_i\le 10^9\)

view solution

题目相当于求前 \(k\) 大的区间的区间的价值之和。对于 \(k\) 大值问题,要么二分答案,要么用堆维护候选答案。注意此处 \(k\) 较大,因此考虑二分答案求出第 \(k\) 大的价值。

假如当前二分价值为 \(x\) 。令 \(L_i\) 表示区间的区间右端点为 \(i\) 时最大的左端点满足 \(val([L_i,i])\ge x\) ,那么价值不大于 \(x\) 的答案就是 \(\sum_{i=1}^nL_i\) 。显然我们有 \(L_1\le L_2\le\cdots\le L_n\) ,因此使用双指针+线段树就能在 \(\mathcal O(n\log 10^9)\) 的复杂度内完成一次check,总复杂度为 \(\mathcal O(n\log^210^9)\)

然而仍然不够优秀。我们考虑换一种思路进行check。枚举右端点 \(r\) 的同时维护每个点作为左端点的答案。当 \(r\gets r+1\) 时,会新加入一个区间。对于某个左端点 \(l\) ,如果原来位置 \(i\) 未被覆盖,而 \(i\) 被新加的区间覆盖,那么答案才会增1。于是维护每个位置最晚的被覆盖时间 \(t_i\) ,如果新加区间覆盖了位置 \(i\) ,则将 \((t_i,r]\) 的答案全部增1。 \(t_i\) 每次都是区间覆盖,因此可以用珂朵莉树维护。然后用线段树实现区间加,每次在线段树上二分找到 \(L_r\) 。可惜的是现在我们的复杂度还是俩 \(\log\)

考虑其实可以预先做一遍珂朵莉树然后记下所有的区间修改。注意到 \(L_i\) 是单调不左移的,因此查询的位置也是单调的。直接用差分代替线段树区间修改,每次暴力右移找 \(L_i\) 即可。

而找到第 \(k\) 大价值后求解原问题其实已经在上面叙述了。

总复杂度为 \(\mathcal O(n\log 10^9)\)

CF997E Good Subsegments

给出长度为 \(n\) 的序列, \(q\) 次询问区间 \([l,r]\) 中有多少子区间满足其是“值域连续”的。举例来说 \([2,1,3],[5]\) 都是“值域连续”的。

\(n,q\le 1.2\times 10^5\)

view solution

数学语言描述:区间 \([l,r]\) 是值域连续的当且仅当 \(\max_{l\le i\le r}a_i-\min_{l\le i\le r}a_i=r-l\)

于是枚举右端点 \(r\) 的同时维护所有左侧点作为左端点时区间的 \(\max-\min-(r-l)\) 这个值。使用单调栈维护后缀最值,新加一个数时直接暴力弹栈后再将对应区间修改即可。可以使用线段树进行维护。发现左端点处值为0时该区间就是合法的,且 \(\max-\min-(r-l)\) 这个值的最小值也是0。于是线段树上维护最小值以及最小值出现次数。询问区间 \([l,r]\) 相当于询问右端点扫描到 \(r\) 时区间 \([l,r]\) 的历史版本和,不过这个“历史版本和”只有在最小值恰为0时才会贡献到答案。将所有询问离线下来做即可。

具体实现比较巧妙。维护懒标记 \(tg2\) 表示该区间的贡献次数,那么当且仅当子区间最小值为0时才下传该标记。不过由于有区间加的影响,因此实际上是子区间最小值和当前区间最小值相同时再下传。具体代码见下:

inline void cov1(int rt,int v){tg1[rt]+=v;mn[rt]+=v;}
inline void cov2(int rt,int v){tg2[rt]+=v;s[rt]+=1ll*v*ct[rt];}
inline void dn(int rt)
{
	if(tg1[rt]!=0)cov1(lc,tg1[rt]),cov1(rc,tg1[rt]),tg1[rt]=0;
	if(tg2[rt])
	{
		if(mn[lc]==mn[rt])cov2(lc,tg2[rt]);
		if(mn[rc]==mn[rt])cov2(rc,tg2[rt]);tg2[rt]=0;
	}
}

CF1148H Holy Diver

\(n\) 次后端插入,然后询问区间 \([l,r]\) 有多少子区间满足 \(\text{mex}=k\) 。强制在线。

\(n\le 2\times 10^5\)

view solution

先不考虑强制在线的限制,离线下来做。

由于 \(\text{mex}\) 的特性是删除数比插入数容易,因此从右向左枚举右端点同时维护左端点答案。设当前再位置 \(r\) ,要删除的数为 \(x\) ,那么我们找到 \(x\) 上次出现的位置 \(pre_x\) ,然后将区间 \((pre_x,r]\)\(\text{mex}\) 全部修改为 \(x\) 。又是区间赋值操作,使用珂朵莉树维护即可。

考虑强制在线的限制。既然我们倒着做的复杂度是可以保证的,那么正着做的复杂度也一定是正确的。现在加入数 \(x\) 后考虑 \(\text{mex}=x\) 的那段区间 \([l,r]\) ,找到最小的 \(v\) 满足 \(v\) 最后的出现位置 \(pos\) 满足 \(pos<r\) ,然后将区间 \((pos,r]\)\(\text{mex}\) 修改为 \(v\) ,之后 \(r\gets pos\) ,重复上述操作直到整段被做完。

那么我们应该如何求答案呢?对每一个 \(\text{mex}\) 值分别考虑,它的出现形式一定为若干个“在一段时间 \([t_l,t_r]\) 内在区间 \([l,r]\) 出现”,即若干个互不相交的矩形。而每次询问则对应于一个矩形求和。于是使用可持久化线段树维护区间和以及区间历史版本和,每次询问时差分下即可。事实上该题有特殊性质,即每个 \(\text{mex}\) 值任何时刻的出现位置一定是一个区间,并且如果删除则一定会将整个区间全部删除。因此历史版本和可以转化为简单的区间加,从而大大减小了时间上和空间上的常数。

「NOI2018」情报中心

给出一棵 \(n\) 个点的边带权的树以及树上 \(m\) 条路经,每条路径都有各自的花费。要求选择两条至少有一条边相交的路径使得路径并的权值减去两条路经的花费最大。若没有这样的两条路径输出 \(\text F\)

多组数据。单组数据 \(n\le 5\times 10^4,m\le 10^5\)\(\sum n\le 10^6,\sum m\le 2\times 10^6\) .

view solution

考虑对于合法的两条路径如何刻画其权值。设两条路径分别为 \((u_1,v_1,w_1),(u_2,v_2,w_2)\) ,则贡献的两倍为(此处的技巧非常有趣,因为减去重复部分事实上很困难):

\[dis(u_1,v_1)+dis(u_2,v_2)+dis(u_1,u_2)+dis(v_1,v_2)-2w_1-2w_2 \]

枚举相交部分中深度最深的点为 \(t\) 。不妨设 \(t=\text{lca}(u_1,u_2)\) ,然后重新审视下贡献(分离变量):

\[[dis(u_1,v_1)-2w_1+dep_{u_1}]+[dis(u_2,v_2)-2w_2+dep_{u_2}]+dis(v_1,v_2)-2dep_t \]

最后一项是常数,可以抛开不管。然后我们令 \(F(v_1)=dis(u_1,v_1)-2w_1+dep_{u_1},F(v_2)=dis(u_2,v_2)-2w_2+dep_{u_2}\) ,那么我们要维护的就是:

\[F(v_1)+F(v_2)+dis(v_1,v_2) \]

这事实上是一个经典问题,即树上带权直径。结论为:两棵树的并的直径两端点属于原先两棵树各自直径两端点形成的集合。据此我们就能快速合并两个集合以及询问两个集合的最远点对。

\(S_u\)\(u\) 子树内的路径提供的匹配候选点集。对当前的 \(t\) ,要计算 \(t\) 的各个儿子子树之间的贡献(注意不要统计到同一个子树的贡献)。每要合并一棵子树时就对该子树的集合和 \(S_t\) 统计最远点对更新答案,然后将子树集合合并到 \(S_t\) 。可以使用线段树进行维护。

对于一条路径 \((u,v)\) ,记 \(t=\text{lca}(u,v)\) 。则 \(u\) 会在 \(S_{[v\to p)}\) 中出现, \(v\) 会在 \(S_{[u,p)}\) 中出现。使用树上差分维护,即在 \(u\) 处插入,在 \(u\)\(p\) 路径上倒数第二个点删除。对 \(v\) 同理。

单组数据复杂度 \(\mathcal O((n+m)\log n)\)

posted @ 2022-03-30 19:05  BILL666  阅读(159)  评论(0编辑  收藏  举报