ARC85-NRE
你有一個序列長度為 $n$ 的序列 $a$,一開始 $a_i=0\ \forall 1\le i\le n$,然後輸入會給你一個長度為 $n$ 的序列 $b$,並且 $b_i \in \{0, 1\}$。
然後輸入會給你可以使用的 $Q$ 筆操作 $l_i,\ r_i$,代表把 $a_l,a_{l+1},a_{l+2},\cdots,a_{r-1},a_r$ 都設成 1。
你可以挑一些操作出來把他給用在序列上,可以全用也可以不用任何操作。
問你操作完的最小 hamming distance 是多少。
其中 hamming distnace 為滿足 $a_i\neq b_i$ 的 $i$ 的數量。
$N, Q\le 2\times 10^5$
這題真的是個酷酷題目。
首先我們考慮 dp,如果 $dp_i$ 為考慮前 $i$ 個答案之類的,在遇到線段相交的情形很容易爛掉。
所以一個維度的 dp 似乎沒辦法解決問題,只好多用一維紀錄當前這格是否為 $1$。
可是如果 $dp_{i,j}$ 的 $j$ 只有紀錄「是不是」的狀態也很難做(你會不知道你的操作右界是作到多少)。
所以改成令 $dp_{i,j}$ 為考慮前 $i$ 格以及所有左界不超過 $i$ 的操作的答案,且最後一個操作的右界是 $j$(如果還沒實行過操作則 $j=0$),換句話說:區間 $[i,j]$ 都是 $1$(如果 $j\lt i$ 代表第 $i$ 格是 $0$)。
那這樣就很好做了呀!
當我們的 $i$ 進到了某個操作的左界,且目前的 $j$ 不超過那個操作的右界,那實施了這個操作的話就會造成 $j$ 被更新。
然後就能順便根據 $b$ 的值來決定當前哪些的狀態答案會增加。更正式的來說令 $cost_{i, j}$ 代表當前的狀態要不要增加答案,則:
$$cost_{i, j}=\begin{cases} 1 & \text{if}\ ( b_i=0\ \&\&\ i\le j)\ ||\ (b_i=1\ \&\&\ j\lt i) \\ 0 & \text{otherwise} \end{cases}$$
接著是 dp 轉移式:
$$dp_{i, j} = cost_{i, j} + \begin{cases} \min_{0\le k\le i} dp_{i-1,k} & \text{if}\ \exists c \text{ such that } (l_c=i\ \&\&\ r_c=j) \\ dp({i-1,j}) & \text{otherwise} \end{cases}$$
如果你像我一樣看到數學式會怕的話可以看 code:
1 | int dp[N][N], b[N]; // dp[i][j] = INF forall i,j |
但我們沒解決根本上的問題:狀態數就已經是 $\mathcal{O}(N^2)$ 了。
這就是這題的特點,雖然沒辦法再減少狀態數了,可是轉移式十分規律。
每到一個新的 $i$,除了那些詢問的右界以外,剩下都只是單純的區間加值。而詢問的右界也不難,只是前綴查詢最小值再單點修改而已。
這些都是可以用線段樹作到的。區間加值的操作只會做 $\mathcal{O}(N)$ 次,而前綴查詢最小值以及單點修改只會做 $\mathcal{O}(Q)$ 次。總共複雜度會是 $\mathcal{O}(NlogN + QlogN)$。
雖然狀態數是 $\mathcal{O}(N^2)$,但由於轉移很規律,所以透過資料結構我們只在以上的複雜度就能把全部的狀態算完了!
1 | /* |