[2020多校A層11.30]選舉
阿新 • • 發佈:2020-11-30
有一個長度為 \(N\) 的字串 \(S[1\dots N]\),它僅由 \(C\) 和 \(T\) 兩種字母組成。
現在有 \(Q\) 個查詢,每個查詢包含兩個整數 \(L\) 和 \(R\) ,表示:設新字串 \(S'=S[L\dots R]\) ,至少在 \(S'\) 中要刪除多少個字元,才能保證:對於 \(S'\) 的每一個字首與每一個字尾,其 \(C\) 的數量都不小於 \(T\) 的數量。
\(N,Q\le 5\times 10^5\)
還不錯的一道題,只不過考試的時候沒來得及仔細想。
考慮怎麼求出這個答案,設 \(C=1,T=-1\) ,我們可以從前往後掃字首和 \(s\) ,如果 \(s\)
這樣貪心一定是對的,因為字首和是負數的話一定刪最後面的,字尾和同理。
我們再進一步觀察這個做法,發現刪除的一個 \(-1\) 既可以在字首被刪除,也可以在後綴被刪除,那麼就相當於存在一個分解點,分界點前的最小字首和是要刪去的 \(-1\) 個數,分界點後的最小字尾和是要刪除的 \(-1\) 個數(取絕對值),並且要最大化。那麼就會有下面這個形式化的式子:
\[ans=\max_{i=l}^{r}(\max_{j=l}^{i-1}(s_{l-1}-s_j)+\max_{k=i+1}^r(s_k-s_r)) \]化簡可以得到:
\[ans=(\max_{k=l+1}^r\max_{j=k}^rs_k-s_j)-(s_r-s_{l-1}) \]發現前面就是一個最大子段和,各種資料結構維護都可以。
Code
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> const int N = 5e5; using namespace std; int n,q,s[N + 5]; char ch[N + 5]; struct node { int pre,suf,sm,ans; }; node operator +(node a,node b) { a.ans = max(a.ans,b.ans); a.ans = max(a.ans,a.suf + b.pre); a.suf = max(a.suf + b.sm,b.suf); a.pre = max(a.pre,a.sm + b.pre); a.sm += b.sm; return a; } struct Seg { node s[N * 4 + 5]; #define zrt k << 1 #define yrt k << 1 | 1 void build(int k,int l,int r) { if (l == r) { if (ch[l] == 'C') s[k].pre = s[k].suf = s[k].sm = s[k].ans = 1; else s[k].sm = -1; return; } int mid = l + r >> 1; build(zrt,l,mid); build(yrt,mid + 1,r); s[k] = s[zrt] + s[yrt]; } node query(int k,int l,int r,int x,int y) { if (l >= x && r <= y) return s[k]; int mid = l + r >> 1; if (x > mid) return query(yrt,mid + 1,r,x,y); if (y <= mid) return query(zrt,l,mid,x,y); return query(zrt,l,mid,x,y) + query(yrt,mid + 1,r,x,y); } }tree; int main() { //freopen("elections.in","r",stdin); //freopen("elections.out","w",stdout); scanf("%d",&n); scanf("%s",ch + 1); for (int i = 1;i <= n;i++) { s[i] = s[i - 1]; if (ch[i] == 'C') s[i]++; else s[i]--; } tree.build(1,1,n); int l,r; scanf("%d",&q); while (q--) { scanf("%d%d",&l,&r); printf("%d\n",tree.query(1,1,n,l,r).ans - (s[r] - s[l - 1])); } return 0; }