Skip to content

Commit

Permalink
CF 587F
Browse files Browse the repository at this point in the history
  • Loading branch information
littlecube8152 committed Oct 27, 2023
1 parent 1a733a0 commit b33e5af
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 1 deletion.
47 changes: 47 additions & 0 deletions content/posts/Codeforces-587F.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
title: "[Codeforces] 587F"
description: "Duff is Mad"
date: 2023-10-28T02:00:00+08:00
draft: false
summary: "笨方塊想學寫程式被爆揍,燒雞"
---

期中考前隨興寫題目寫到一題字串題,最終用了很奇怪的複雜度過了,自己還算錯一次,覺得這樣值得紀念所以寫了這篇文。

## 題目敘述
[Link](https://codeforces.com/contest/587/problem/F)
有 $n$ 個由小寫字母組成的字串 $s_i$,支援 $q$ 筆詢問:
- 讓 $\mathrm{occ}(t, s)$ 表示字串 $s$ 出現了幾次 $t$,給定 $l, r, k$ 回答 $\sum_{i=l}^{r}\mathrm{occ}(s_i, s_k)$.

$1 \leq n, q, \sum |s_i| \leq 10^5$

## Weird Solution

考慮離線並對字串的長短分塊,假設 $N = \max (\sum |s_i|, q)$。

- Case 1. $s_i \geq K$
可以將所有字串丟進 AC 自動機中求得所有 $\mathrm{occ}(s_j, s_i)$,直接用前綴和離線回答答案。時間複雜度 $O\left(N \cdot \frac N K\right)$。
- Case 2. $s_i \leq K$
一樣丟進 AC 自動機中匹配,不過事先對自動機上節點預處理「不斷跳 fail link 第一個遇到的字串結尾節點」,這時候每次匹配一個新的字元的時候,一口氣按照這個預處理的結果不斷跳下去並蒐集所有節點。
求答案時可以直接對蒐集到的所有節點計算「某個節點上有多少位於 $l, r$ 之間的字串尾」。時間複雜度 $O\left(N \cdot K^2 \log N\right)$。

這樣總時間複雜度取 $K = \left(\dfrac{N}{\log N}\right)^{\frac 1 3}$,會得到 $O\left(N^{\frac 5 3}\log^{\frac 1 3} N\right)$,空間只需要 $O(N\Sigma)$ 開自動機。

實際寫下去會過(感覺是沒有人特別卡,不過這個作法應該不至於太糟,應該還是會過的)。

小插曲是,我以為 Case 2 的其實只有 $O\left(N \cdot K \sqrt K \log N\right)$,不過實際上「跳 $\sqrt N$ 次就會結束」必須要以總共的字串長度來看,所以算出了另一個奇怪複雜度,不過代進去實際數字其實差不多就是了,所以沒有差太多。

## Better Solution

事實上 Case 2. 有個更好用的性質,也就是利用 fail link 會形成一棵樹,根會在起始節點,所以上面的問題會變成一個掃描線問題,每個節點要查詢的會是根到祖先的路徑,每個詢問要查詢的就是那一堆走到的狀態分別到祖先的路徑上全部的總和,也就是說,我們要解下列的問題:

有一棵定好根的樹,我們要想辦法處理:
- $O(N)$ 次單點加值
- $O(NK)$ 次查詢根到祖先的和

第一個操作顯然可以貴一點,所以我們考慮對 DFS order 分塊,並讓單點加值變成子樹加值,而路徑詢問變成單點詢問,所以 Case 2 就可以被壓進 $O(NK)$,總時間複雜度來到乾淨的 $O(N \sqrt N)$。

## Reference Code

[Weird Solution](https://codeforces.com/contest/587/submission/230061757) (注意那個註解是我之前算錯複雜度的,所以以這篇為主)
[Better Solution](https://codeforces.com/contest/587/submission/230062033)
22 changes: 21 additions & 1 deletion content/posts/pre-IOI-journal.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "國培日記"
date: 2023-07-31T23:00:00+08:00
draft: false
draft: true
tags: ["camp","journal"]
summary: "希望笨方塊可以不要在 IOI 燒雞 > <"
---
Expand Down Expand Up @@ -52,4 +52,24 @@ String Index 其實是某個叫做 Burrows-Wheeler Transform 的東西,簡單

晚餐我點了一碗炒泡麵和一碗滷肉飯,但來的時候我突然食量變超小連半碗炒泡麵都吃不完,還好一邊 virtual 一邊吃之後到快半夜終於吃完了,我下次不要再亂點一大堆東西了 QQ

## Day 5

早上 checkout 和選旅的時候他好像沒有收杯子的錢,感謝和選旅 > <

我沒有很認真地聽早上的 Boolean Complexity 在幹嘛,因為教授問了那個至少 $\frac E 2$ 的 cut 問題之後我給他之前聽過很多次的選最多那邊的策略,然後它明明是對的但教授可能想要講他想講的一直說我的方法只是 constructive 沒有證明,但是我明明就有好好證明給他看這個作法是好的,所以我就有點不想理教授,後來他好像講了一些奇奇怪怪的 NAND circuit 問題但我沒有仔細聽他在幹嘛。

下午原本是練習賽但是卡車說讓我們打我們想要打的比賽,所以我們就選了一場兩小時半的 Div. 1 打,我自己真的覺得比較以前的 Div. 1 我比較想打,最近的雖然說是有一些還不錯的題目,但我自己比較不喜歡 ARC 或者 Codeforces 這類風格的題目,我有點想說是我不會猜那些東西但可能比較像是我跟這類的題目不合,因為他們畢竟應該還是能想出來的。

晚上的時候坐高鐵去台北,等高鐵的時候新竹風真的很大但吹起來很舒服,不過我會這樣覺得可能是因為我不住新竹,或許當地人會覺得這沒什麼或者覺得一直有很強的風很糟。

## Day 6

今天是在師大跟 8w 學寫程式,同時也是 ICPC 培訓班第一次練習但我比較想打 OI(8w 也比較推薦 OI)所以後來就是我們六個人在培訓班說明之後打 JOIOC 2023 Online Mirror,開場的時候我倒過來讀因為 A 是互動題然後 JOI 系列的互動題都噁噁的,B 是奇怪的周長,把 B 的圖在紙上亂畫之後就覺得很噁,所以我先去試看起來最可做的 C,第一次畫 C 畫完之後發現我看錯題目,這時候我就發現有效的東西只有 $2D \times 2D$,所以可以有一堆枚舉答案的方法,想一想之後我覺得我想到了,看完範測的圖才發現我又看錯一次,剛剛的解法直接爛掉,還好我很快就想了一個 $\mathcal O(D^2 \min (D, M))$ 的作法,傳上去之後拿到 75 分,B 還是沒有任何想法所以我決定先去把暴力寫出來看,但是我把 `pii(x + dx, y + dy)` 打成 `pii(x + dx, x + dy)` 花了一段時間才發現到我不會打字,修完傳上去就拿到 16 分了,這時候 B 有一個應該是用數學就能做掉的解跟很多 case 的掃描線可以喇,然後 A 完全不知道是什麼自動機怪題,總之為了理解題目先把 10 分拿到,拿到之後我覺得 A 還有很多喇分的空間,所以我想先把 B 能喇的分數拿到,要是沒有下一步的想法的話就直接全力喇 A 的分數。後來想一想想到詭譎的兩個策略:前 $500$ 個先暴力做,後 $500$ 選兩種策略裡面最好的,第一種是假如 $S_{i - 1}$ 跟 $S_{i - j - 1}$ 不一樣,那我可以每 $j$ 個蓋一個環,然後在這些 $i - 1, i - j - 1, i - 2j - 1, \cdots$ 的地方跳掉,假如有兩個相鄰的人長一樣那他們可以共用一個環就好,所以這樣可以用在假如前綴相當亂的時候,很容易就能找到一組東西;第二種是如果我們有很多連續的 0 跟 1,那我們可以用一直走然後遇到不同的跳到下一個的方法,但這樣的問題是可能在接近要問的人的時候很多人一樣,所以我們就直接往後蓋一條鍊,在遇到新的相異字元時跳過去那個鍊,這樣我們就能知道這個鍊有多長了。第一次寫完傳上去拿到 10 分,原本我想要直接 reject 我的這個唬爛,但是我突然想到我第一個策略的 cost 算錯,把它改完之後就拿到 37 分了,這時候我想應該 A 就是我的唬爛極限了,所以我回頭去壓 C 的常數結果壓到兩筆沒過,不知道是我真的快壓過了還是其實沒有。

結束之前我覺得應該 B、C 某一題沒有那麼難但我不會,結果是大家都打差不多但 8e7 特別強,可是我覺得我應該要想到 C 的滿分。最後的 rank 應該可以在 JOIOC 的網站找到。

晚上的時候我在洗衣服跟看邱翊均玩 TJ 以及拖稿國培日記。

## Day 7


0 comments on commit b33e5af

Please sign in to comment.