Three Occurrences 题解
文章目录
Solution1 (Hash)
By neal
我们先考虑另一个问题:
有一个长度为 n n n的序列 a , ( 1 ≤ a i ≤ n ) a,(1\leq a_i\leq n) a,(1≤ai≤n),你要计算有多少对 ( l , r ) (l,r) (l,r)满足 a l , a l + 1 . . . a r a_l,a_{l+1}... a_r al,al+1...ar中每一个数出现的次数是 3 3 3的倍数?
这个问题我们可以考虑 h a s h hash hash,我们先把 n n n个随机值装进 r a n d rand rand数组里,然后从前往后计算颜色 c c c在 i i i及之前出现的次数 m o d 3 \mod 3 mod3的值,记为 s u m i , c sum_{i,c} sumi,c。由于 [ l , r ] [l,r] [l,r]中每一个数出现的次数 m o d 3 \mod 3 mod3都 = 0 =0 =0,所以满足 ∀ k , ( k ∈ [ 1 , n ] ) s u m r , k = s u m l − 1 , k \forall k,(k \in [1,n]) sum_{r,k}=sum_{l-1,k} ∀k,(k∈[1,n])sumr,k=suml−1,k。
我们可以设 v a l i = ( ∏ j = 1 n s u m i , j ∗ r a n d j ) m o d A val_{i}=(\prod _{j=1}^n sum_{i,j}*rand_{j}) \mod A vali=(∏j=1nsumi,j∗randj)modA
然后放进一个hash table 就行了。
那么原问题怎么解决呢?
我们已经能够让所有的颜色出现的次数是3的倍数,但是我们还差要使得出现的次数 ≤ 3 \leq 3 ≤3。
不过这也没有关系,因为我们可以把hash 值存在一个vector里,然后从前往后扫描,遇到不行的就pop_front。
时间复杂度 O ( n ) O(n) O(n)。
参考资料:https://codeforces.com/blog/entry/82643
Solution 2(Segment Tree)
By MrGary
我们从大到小枚举有边界 r r r。
如果存在左边界 l l l满足要求,那么对于每一个颜色 i i i来讲,都是两端区间:
- 在所有 r r r左边的颜色为 i i i的位置,的第一个,和 r r r(左开右闭)
- 在所有 r r r左边的颜色为 i i i的位置,的第四个,和第三个(左开右闭)
特殊的:如果不存在用0代替。
这样我们可以在线段树上区间加,和查找恰好被覆盖 n n n次的点(覆盖最多的点)的个数。
线段树+懒标记。
时间复杂度 O ( n ∗ log 2 ( n ) ) O(n*\log_2(n)) O(n∗log2(n))
参考资料:https://codeforces.com/blog/entry/82643?#comment-696498
Solution 3(Sweep Line & Segment Tree)
By errorgorn
与MrGary的那个比较相似就是处理出所有颜色可以的左端点范围,然后用线段树找出来。
时间复杂度也是
O
(
n
∗
log
2
(
n
)
)
O(n*\log_2(n))
O(n∗log2(n))的
参考资料:https://codeforces.com/blog/entry/82643?#comment-696298
Solution 4(Trit-wise)
By pikmike
维护三进制前缀和: s u m i = ∑ i = 1 n a i ∗ r a n d i sum_i=\sum_{i=1}^n a_i*rand_i sumi=∑i=1nai∗randi,其中用trit-wise(类似于 x o r xor xor)来表示, r a n d i rand_i randi是一个平均值。
可以发现如果一个数出现了 m m m次 = = =一个数出现了 m m o d 3 m\mod 3 mmod3次的值。
然后就比较像前面的neal的hash了。
时间复杂度 O ( n ∗ l o g 2 ( n ) ) O(n*log_2(n)) O(n∗log2(n))。
参考资料:https://codeforces.com/blog/entry/82673
总结:
这是一题不错的数据结构题,也可以帮助大家理解hash。
注:所有代码可以在参考资料中找到