|
|
$n\le 100$对于询问 $[l,r]$,暴力枚举 $l',r'$,然后暴力计算,时间复杂度为 $O(mn^3)$。 或者简单优化一下到 $O(mn^2)$。期望得分 $10$。 $n\le 1000$先预处理出所有区间的权值 $v_{l,r}$。 然后可以用各种做法求出所有区间的子区间的权值 $ans_{l,r}$。 时间复杂度为 $O(n^2+m)$。期望得分 $30$。 特殊性质按位与,按位或,$\gcd$ 三种运算有一个很厉害的性质,以 $\gcd$ 为例:对于一个数 $x$,不断令 $x\gets \gcd(x,v)$,则 $x$ 值发生变化的次数为 $\log V$ 次。 具体的原因可以均摊分析,考虑 $x$ 的质因子可重集合,显然其大小不超过 $\log V$,每次取 $\gcd$,这个集合要么不变,要么大小至少减一,只有集合大小改变才会影响 $x$ 的值,所以 $x$ 值发生变化的次数为 $\log V$ 次。 按位与和按位或也是如此,每一位上只会从一种值变成另一种值,所以变化次数也不超过 $\log V$。 数据随机后,若一个区间长度超过 $100$,答案就极有可能变成 $0$ 了。 然后乱搞一下应该可以得到 $40$ 的分数。 $n\le 10^5$考虑离线扫描线。 有这样一种经典扫描方式,令 $r:1\to n$,假设当前扫到 $r$,我们让 $\forall l \in [1,r]$ 的位置 $v_l$ 加上区间 $[l,r]$ 的权值。 这样若对于询问 $[L,R]$,若 $r$ 扫到了 $R$,则查询 $v_{L\sim R}$ 的区间和即可。这是非常经典的增量贡献求答案的方法。 回到本题,使用上面所述的扫描线算法,因为上述特殊性质的存在,假设当前扫到 $r$,设 $A_i$ 为 $i\sim r$ 的区间按位与的和,$B_i,C_i$ 同理,则这三个数组分别可以划分为 $\log V$ 段,每一段内值相同。 这样我们得到 $3\log V$ 断点,设 $D_i=A_iB_iC_i$。则 $D_i$ 也被划分成 $\log V$ 段,每一段内部值相同。 那么我们令 $\forall i\in [1,r],v_i\gets v_i+D_i$,这样更新过程可以转化成对 $v$ 数组的 $\log V$ 段区间加法。 查询答案即为查询区间和,找断点可以二分加线段树,总而言之时间复杂度为 $O(n\log^2 n+m\log n)$,期望得分 $80$。 如果你用下文所述找断点的方法,用树状数组常数小的话也可以得到 $100$。 $n\le 5\times 10^5,m\le 5\times 10^6$考虑正解,静态询问,且 $q\le 5\times 10^6$,应该要做到 $O(1)$ 回答询问,考虑离线扫描线。 有这样一种不经典的扫描线方法:维护一个指针 $r$ 从 $1$ 扫到 $n$,每扫到一个位置就回答右端点在 $r$ 的询问的答案。考虑此时在第 $l$ 个位置上维护 $w_{r,l}$ 表示 $[l,r]$ 所有子区间的价值之和。 不难发现我们把题目又读了一遍。考虑当 $r-1\to r$ 时,$w_{r,l}$ 相比于 $w_{r-1,l}$ 的答案应该有怎样的变化。 注意 $w_{r,l}$ 的理解,可以理解成当指针扫到 $r$ 时的版本时,第 $l$ 个位置的值,并不需要真正维护二维数组,只需要滚动维护第二维即可,下文同样有类似的表达方法。 令 $A_{r,i}$ 表示扫到 $r$ 时 $i\sim r$ 的所有数按位与之和,$B_{r,i},C_{r,i}$ 同理,则需要更新: $$w_{r,i}\gets w_{r-1,i}+\sum_{j=i}^rA_{r,j}B_{r,j}C_{r,j}$$ 先考虑如何维护 $A_{r,i},B_{r,i},C_{r,i}$。不难发现,当 $r\to r+1$ 时,发生更改的 $A_{r,i},B_{r,i},C_{r,i}$ 都是 $1\sim r$ 的一段后缀。 对于 $x<y$,若 $C_{r+1,y}=\gcd(C_{r,y},c_{r+1})=C_{r,y}$,则显然有 $C_{r+1,x}=\gcd(C_{r,x},c_{r+1})=C_{r,x}$,因为 $C_{r,x},C_{r,y}$ 和 $C_{r,y},c_{r+1}$ 在集合上都是子集关系。 当 $r\to r+1$考虑这样一种更新方式,从 $r$ 出发,用 $r+1$ 的值倒着更新 $r-1,r-2,\dots$ 的位置,直到 $A_{r,i},B_{r,i},C_{r,i}$ 都不发生改变,则更新完成。 分析复杂度,以 $A_{r,i}$ 为例,这个位置对最终复杂度有贡献当且仅当某次更新时 $A_{r,i}$ 发生了改变,根据最上面的分析,这种改变次数是不超过 $\log V$ 的,所以更新的总的复杂度为 $O(n\log V)$。 此时,当 $r-1\to r$ 时,维护一个前缀和 $s_{r,i}$: $$s_{r,i}=\sum_{j=1}^nA_{r,j}B_{r,j}C_{r,j}$$ 则更新可以表示为: $$w_{r,l}\gets w_{r-1,l}+s_{r,r}-s_{r,l-1}$$ 令 $v_i=s_{i,i}$,则有: $$w_{r,l}=\sum_{i=l}^rv_i-\sum_{i=l}^rs_{i,l-1}$$ 前面的部分显然可以单独维护,后边的部分则是查询 $s$ 所有版本在位置 $i$ 上的历史和。 因为我们每次修改都是暴力修改 $s$,所以问题变成: 1. 单点修改某个位置的值 $s_i$。 2. 单点查询某个位置的历史和 这个东西已经非常简洁了,考虑第 $i$ 个位置的历史和 $hs_i$,$s_i$ 上一次修改的时间 $lst_i$,若在时刻 $t$ 修改 $s_i$ 为 $val$,则: $$hs_i\gets hs_i+(t-lst_i)s_i,s_i\gets val,lst_i\gets t$$ 则在 $t$ 时刻查询位置 $i$ 的历史和为: $$hs_i+(t-lst_i+1)s_i$$ 然后我们惊奇的发现这个问题就解决了。 时间复杂度为 $O(n\log n+m)$,期望得分 $100$。 代码实现首先是 $\gcd$,可以用 C++ 自带的 `__gcd` 函数,也可以手写二进制优化更相减损术的 $\gcd$,两种写法各有优劣。 然后是快读快输,这个题已经卡常到普通的 fread 和 fwrite 已经无法满足了,建议找个又臭又长的封装版快读快输使用。 然后将询问储存:对于储存询问,不建议将左右端点分开两个数组储存,建议直接用结构体或者 pair 来储存询问左右端点(我也不知道为什么啊,可能是寻址更快吧)。 然后是将离线:不要用 vector,可以用链表在端点处挂上询问。不要用 sort,可以用计数排序。 luogu 的原题需要严厉的卡常,这里不知道是否需要。
题目4251 我能在摸鱼被发现的情况下躲避教练的视奸吗
AAAAAAAAAA
4
评论
2026-02-07 16:48:45
|