跳转至

2018 ICPC Asia Jakarta Regional Contest

排名 当场过题数 至今过题数 总题数
29/443 10 12 12

B

upsolved by JJLeo

题意

\(n\) 个节点的树,\(q\) 次以下三种操作之一:

  • 删掉一个未被删除的点。
  • 复原一个删掉的点。
  • 选择一个未被删除的点,对该点所在连通块中的所有点,距离该点距离为偶数的权值 \(+x\),距离为奇数的权值 \(-x\)。同时输出该连通块中的点数。

一条边存在当且仅当两个端点都没有被删。

如果一个点最终被删掉了,则它的权值和为删掉之前的权值。一个点被复原它的权值也为它删掉之前的权值。

所有操作结束后,输出所有节点的权值和。

(\(1 \le n,q \le 10^5\))

题解

随便确定一个点为根。

距离该点距离为偶数的权值 \(+x\),距离为奇数的权值 \(-x\) 这个操作,可以简化为:

  • 如果该点深度为偶数,则该连通块所有点权值 \(+x\)
  • 如果该点深度为奇数,则该连通块所有点权值 \(-x\)

最后将所有深度为奇数的点权值变为负数即可。

首先需要一种算法找到 \(x\) 所在连通块没被删的点中深度最小的点 \(y\)

一种方法是树剖+树状数组,不断跳找到第一个被删节点的重链后,在这条链上二分。

另一种更好写的方法是,一个点被删就对子树中每个点 \(+1\),复原再 \(-1\),每个点的权值即为有多少个祖先被删,倍增找到第一个权值不同的祖先即可。

两种方法复杂度均为 \(O\left(\log ^2 n\right)\)

接着依次处理两个查询操作:

增删节点,多次询问每个连通块大小

\(\textit{siz}_i\) 是以 \(i\) 为根的子树的大小,\(T(i)\)\(i\) 为根的子树的中点的集合。

通过维护每个点的一个权值 \(s_i\),使得 \(\displaystyle \textit{siz}_x- \sum_{i \in T(x)} s_i\) 等于以 \(x\) 为根子树中和 \(x\) 连通点的数量。如果被删的节点之间没有祖先关系,那么直接令被删点 \(i\) 的权值为 \(\textit{siz}_i\) ,其它点 \(s_i=0\) 即可。如果出现了祖先关系,那么这样会算重,需要进行一些调整:

  • \(x\) 被删除时,设 \(x\) 所在连通块没被删的点中深度最小的点为 \(y\),令 \(s_x=\displaystyle \textit{siz}_x- \sum_{i \in T(x)} s_i\),若 \(y\) 不为根则令 \(s_y\) 减去 \(s_x\)
  • \(x\) 被复原时,设 \(x\) 所在连通块没被删的点中深度最小的点为 \(y\),若 \(y\) 不为根则令 \(s_y\) 加上 \(s_x\),再将 \(s_x\) 变为 \(0\)

这样类似差分思想的处理,可以始终满足我们的需求,因此询问 \(x\) 所在连通块大小时,找到 \(x\) 所在连通块没被删的点中深度最小的点 \(y\)\(\displaystyle \textit{siz}_y- \sum_{i \in T(y)} s_i\) 即为答案。

增删节点,多次给一个点所在连通块的每个点增加一个值,最后只询问一次所有点权值和

每个点维护两个权值 \(a_i\)\(b_i\)

找到 \(x\) 所在连通块没被删的点中深度最小的点 \(y\),直接给 \(y\) 子树中每个点 \(a_i\)\(b_i\) 均增加对应的权值。当点 \(x\) 被删时将 \(b_x\) 设为 \(0\),当点 \(x\) 被复原时,将 \(x\) 子树中所有点 \(a_i\) 减去 \(b_x\),相当于把不连通多加的部分减掉。

当被删的节点之间没有祖先关系时,上述做法是正确的,否则如果先复原 \(x\) 再复原 \(y\)\(y\)\(x\) 的祖先,那么就会让 \(x\) 子树中的点多减去 \(y\) 祖先增加的权值。

因此需要进行调整,我们对 \(b_i\) 的含义进行重定义,代表 \(i\) 被删后所有 \(i\) 的祖先给 \(i\) 子树多加的权值和,同时在节点增删时进行一些操作:

  • \(x\) 被删除时,设 \(x\) 所在连通块没被删的点中深度最小的点的父节点为 \(y\),则 \(y\) 也被删除,根据定义,令 \(b_x=b_y\);特别地,若 \(y\) 不存在 (即 \(x\) 没有祖先被删除),令 \(b_x=0\)
  • \(x\) 被复原时,设 \(x\) 所在连通块没被删的点中深度最小的点的父节点为 \(y\),则 \(y\) 也被删除,则只让 \(x\) 中子树减掉 \(x\) 祖先但不是 \(y\) 祖先这些点所多加的值,即为 \(b_x-b_y\);特别地,特别地,若 \(y\) 不存在 (即 \(x\) 没有祖先被删除),减去 \(b_x\)。此外,需要注意,根据 \(b_i\) 的定义,子树中不仅 \(a_i\) 要减去该值,\(b_i\) 同样要减去该值。

最后将所有被删除的点再复原,那么点 \(i\) 的权值即为 \(a_i\)

上述操作均可以用 dfn 序 + 树状数组完成 (单点修改区间查询和单点查询区间修改),复杂度瓶颈在找到 \(x\) 所在连通块没被删的点中深度最小的点 \(y\),因此总时间复杂度为 \(O\left(n \log n + q \log ^2 n\right)\)

C

upsolved by JJLeo

题意

构造一个最短的字符串 \(\in {\{0,1,\ldots,m-1\}}^*\),要求至少出现了 \(k\) 个长度为 \(n\) 的子串。(\(1 \le n \le 10^5\)\(1 \le m \le 10\)\(1 \le k \le \min\left(m^n,10^5\right)\))

题解

字符串最短为 \(n+k-1\),这样才能有 \(k\) 个长度为 \(n\) 的子串。考虑如下的有向图:

  • 每个字符串 \(s \in {\{0,1,\ldots,m-1\}}^{n-1}\) 代表一个点,\(s \overset{a}{\to} t\) 存在当且仅当 \(s\) 删掉第一个字符、在末尾添加 \(a\) 后等于 \(t\)

显然每个点的入度出度均为 \(m\),因此这个图有欧拉回路。

随便选一个节点当起点,设该点的字符串为 \(s\),接着遍历欧拉回路,依次将边上的字符添加到 \(s\) 后面。将最后得到的字符串看作环,每个字符串 \(s \in {\{0,1,\ldots,m-1\}}^n\) 恰好出现了一次。这是因为图中每个点一条指出去的边都唯一确定了一个 \(s \in {\{0,1,\ldots,m-1\}}^n\),一共有 \(m^n\) 个这样的二元组,对应 \(m^n\) 个字符串。遍历整个欧拉回路的过程相当于不断滑动长度为 \(n-1\) 的窗口。

关于寻找欧拉回路,首先复习一下 Hierholzer 算法:

从起点出发 dfs,每经过一条边就将其删掉,每个点回溯时入栈,最后从栈顶到栈底输出栈中元素即得到欧拉回路。

上述流程等价于每次删掉一个环,那么剩下的图还是欧拉图,由归纳假设可得算法的正确性。

本题我们只需找到长度为 \(k\) 的欧拉路径,当 \(m^n\) 较大时不可能把整个欧拉回路跑出来,因为在回溯时才入栈,调用上述算法有可能递归 \(m^n\) 次而栈内点数仍 \(<k\)

因为上述算法只是一个简化版,完整版的 Hierholzer 需要维护两个栈 \(a\)\(b\),每遍历到一个点将其加入 \(a\),回溯时将该点从 \(a\) 中弹出加入 \(b\),那么任意时刻 \(a\) 从栈底到栈顶以及 \(b\) 从栈顶到栈底都是一条合法的欧拉路径。

这样至多遍历 \(O(k)\) 个点就能得到答案了。实现过程中,使用 deque 记录当前点对应的字符串,并在递归函数内记录入边上的字符即可。因为 \(m\) 较小,直接用一个 set 记录每个长度为 \(n\) 的字符串是否出现来实现记录每条边是否被删,并暴力遍历,总复杂度为 \(O(km \log k + n)\)

题解中当 \(n\) 较大时直接输出随机字符串,实际上并不需要。

E

solved by Bazoka13

题意

题解

F

solved by

题意

题解

G

upsolved by

题意

题解

H

upsolved by

题意

题解

J

upsolved by

题意

题解

K

upsolved by

题意

题解

记录

0h:开局有人过I,感觉假人,ZYF喂了个L题,MJX扔了个A题。过5min左右一堆人过了I确实是个大签到,CSK过了。L题范围只有60,暴力DP就好了,写完MJX和ZYF看了会不知道哪错了,发现题里要求没有前导0,改了就过了。MJX搞了个做法,一个情况处理不了,CSK搞了个小数据的方法改了下就可以了。互相喂了几个题的题意,看榜有几个人过了D,扫了一下题面和样例感觉是个sb题,ZYF和MJX想了想有好多corner case,加了一堆感觉差不多就交了。WA了后有一个corner case漏了,改了过了。

1h:ZYF搞了个J的做法,改了个状态就开写了,MJX看K就是一个经典题(沈阳的)。CSK搞了搞G的 \(O(n^3\log{n})\) 的做法,被 ban 了,H开始只会暴力,润了。CSK开始看E,把EF的题意喂给MJX。ZYF把J做完MJX把做法喂给ZYF,ZYF把K也过了。

2h:CSK说了一下E的大致做法,MJX YY了一下感觉无比正确,CSK开冲!MJX ZYF挂机GH,感觉G可以暴力 \(O(n^3)\) 做,但是暴力用 vector 存会 MLE,ZYF直接写了个链表来存,空间就很小了但是一直调不对。隔壁训练结束了,mian记错时间了,没人来主持会议,MJX开始电话轰炸。

3h:CSK写完了MJX观察了一波感觉大概挺对的交了WA了,ZYF改不出来了直接写了个vector不管了,大概在极限数据下卡到了256MB下,交了过了,数据爆弱就只用了50MB,迷惑。CSK改了下变成T了,MJX ZYF一起看感觉复杂度对对的,应该被卡常了,CSK改了一堆STL,然后最后交了个C++20飞快的过了,之前那个直接改成c++20也过了,迷惑。ZYF想了想H巨简单,给MJX过了一下就直接写了。MJX YY了一个F的做法,感觉很对,让CSK check了一下扔给ZYF去写了。

4h:第一发WA了,CSK随手构造一个hack的数据,ZYF改完过了。剩了个BC,MJX感觉B完全不可做,去乱搞C了。想了想都不咋会,直接开始随机了,然后果然就T了,测了一下个数比较满的时候就巨慢,不会了,寄!

After end:G std竟然是 \(O(n^3\log{n})\) 的,不能接受!

总结

Dirt

回到页面顶部