[Ynoi2013] 大学

非常好之 lxl 使我的代码旋转。

看到这个题,第一反应显然是如果我们能够每次确切的找到要除的数,然后用树状数组进行单点修改,那么就可以达到 O(nlogVlogn) 的复杂度。

那么接下来就是考虑如何去找到能除的数。

首先,我们不难想到对于每个权值 v,将他在数组中所有的倍数开一个 vector 存起来。容易发现,一个数因子个数大概在 log2n 级别,所以这样没有问题(当然应该用埃筛的思想进行储存,否则会被卡常)。

然后,我们发现,经过一次操作之后,我们需要将对应倍数的数进行删除。也就是说,当我们要除以 x 时,我们就会对对应的 x 中的元素进行遍历,如果当前遍历到的数因为前面的 x 已经不是 x 的倍数了,那我们就把他从这个 vector 中删除,从而来保证复杂度。否则的话,就暴力树状数组修改。

接下来就是如何维护删除。由于我们每次是对 l,r 进行操作,所以我们需要先二分查找到第一个大于等于 l 的位置进行操作,故不能使用链表。

一个好的解决方法时平衡树,但是 lxl 他不同意,所以考虑传统艺能并查集,用并查集去维护删除的数(一个很常见的 trick 我竟然不知道)。

例如,对于每一个序列下标开一个 father,例如有四个数时: 1,2,3,4,最初都完整的存在。

然后,如果我们要删除 3,我们就考虑 fafindfa3=findfa(3+1)fa 数组也就变成了 1,2,4,4

也就是说删除 i 的时候,我们就令 fafindfa(i)=findfa(i+1)

枚举的时候,i 的下一个就是 findfa(i+1)

这样就可以规避掉删除的问题,不用去维护平衡树,并得到时间复杂度的保证,以及要将 x=1 的情况直接特判掉。

给一下核心代码吧:

for (int i = findroot(x, (lower_bound(p[x].begin(), p[x].end(), l) - p[x].begin())); i < p[x].size() && p[x][i] <= r; i = findroot(x, i + 1))
{
	int now = i;
	if(a[p[x][now]] % x == 0) add(p[x][now], a[p[x][now]] / x - a[p[x][now]]), a[p[x][now]] /= x;
	else fa[x][now] = findroot(x, now + 1);
}

Submission

posted @   Saltyfish6  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
Document
点击右上角即可分享
微信分享提示