24/11/30 ABC381+莫隊+分塊+整體二分學習筆記

Lunar_Whisper發表於2024-11-30

[ABC381D]

好題。
由於題目描述中提到了取出的子序列長度必須是偶數個,所以可以考慮對於原來的\(a\)序列每次進行長度為\(2\)的尺取,這時候不難發現對於開頭位置具有兩個答案,一個是從\(1\)為開頭,一個是從\(2\)為開頭,所以寫個函式封裝一下就好了,類似這種cout << max(solve(1),solve(2))

接下來考慮在尺取過程中如何計算答案的貢獻,設當前列舉到的區間開頭為\(l\),結尾是\(r\),每個數出現的次數是\(cnt_i\),擁有以下兩種情況:

  • 第一種:\(a_r \ne a_{r+1}\)
    針對這種情況,顯然是對於\(a_r\)\(a_{r+1}\)這兩個位置都不可取(Q:為什麼不可以取\(a_{r+1}\)呢? A:若取了\(a_{r+1}\),那麼後面答案計算出來的都是成對出現的,一定是偶數個,加上這個序列長度就會變成奇數個),既然這兩個位置不可取,那麼所有包含他倆的子序列都不可取,所以將\(l \to r\)這段區間所有的數都扔掉,即這段區間所有數的出現次數\(-1\),同時將\(r\)變成\(r+2\)(即下個尺取的地方),\(l = r\)
if(a[r] != a[r+1])
{
	for(int i = l;i < r;i++) cnt[a[i]]--;
	r = r+2;
	l = r;
}
  • 第二種:\(a_{r} = a_{r+1}\)
    針對這種情況,我們對於\(l \to r\)這段區間還要滿足第三個條件,也就是題目中說到的所有數出現次數不是\(0\)次,就是\(2\),直接從\(l\)開始,不斷地將\(l + 2\)並同時減去\(a_l\)\(a_{l+1}\)出現的次數,直到滿足\(cnt_{a_r} = 2\),最後將\(r+2\),變動一下就好了。
else
{
	cnt[a[r]]++,cnt[a[r+1]]++;
	while(l < r && cnt[a[r]] > 2)
	{
		cnt[a[l]] -= 2;
		l += 2;
	}
	r += 2;
}

那麼最後對於所有計算過程中算到的答案取一個\(\max\)即可。

[ABC381E]

好題。
如果將題面抽象一下的話,其實跟龍之研習差不多,比較有意思,如果做過這場ABC的C題的話,有可能會受到啟發。

我們考慮將每一個/的位置用陣列記錄下來,針對每一個/所產生的答案,顯然為(其左側\(1\)的個數和其右側\(2\)的個數)\(\times 2+1\),隨著/位置的向右側移動,顯然發現\(1\)的個數在單調遞增,\(2\)的個數在單調遞減,可以考慮二分,二分方法如下:

用陣列記錄每個/的位置,二分出任意一個/,判斷這個位置是否在查詢的區間內,不在的話就移動\(l\)\(r\);提前預處理出針對每一段區間\(1\)的個數和\(2\)的個數,顯然可以用字首和,\(O(1)\)計算當前/左側\(1\)的個數和\(2\)的個數,貢獻計算方式上面寫了,對於單調性問題,我們顯然是儘可能讓右側的\(1\)的數量增加,故當\(c_1 <= c_2\)時,l=mid+1

Submission

相關文章