[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