Codeforces 1340F - Nastya and CBS(分塊+雜湊)
阿新 • • 發佈:2021-08-01
分塊+雜湊
個塊,單次查詢的複雜度又退化到了 \(\mathcal O(n)\),穩穩地 T 掉。一種解決方法是指標,可作為一名從剛學 OI 開始就不喜歡指標的選手自然是不會選擇這樣的寫法的,因此這裡介紹一種不用指標的寫法。考慮我們暴力合併的過程,對於一個塊而言我們肯定是先用這一塊的一段右括號去消前面的一段左括號,再加入一段右括號,而如果我們把每次加入的一段右括號視為一個連續段的話,那麼每次消除左括號時最多將一個左括號的連續段劈成兩段。這樣思路不就出來了嗎,我們考慮對於每一塊,將這一塊消除後剩餘的左括號壓入一個 塊消除後剩餘的左括號序列的第 \(l\) 至第 \(r\) 個元素,然後我們維護一個棧儲存這些三元組儲存這些未消完的括號組成的連續段,這樣每次與一段長度為 \(L\) 的右括號序列進行消除時只需取出棧頂的 \(L\) 個元素,雜湊判斷括號序列是否相等即可,如果最後一段消完後還有剩餘就將剩餘部分壓入棧中。根據之前的推論,每個三元組恰好進出棧各一次,而每次消除最多增加一個三元組,因此總複雜度是嚴格 \(\mathcal O(m\sqrt{n})\) 的。
首先看到這樣的資料範圍我們可以考慮分塊,具體來說,對於每一塊我們記錄其中的括號是否能完全消掉,以及對其進行括號相消之後的括號序列(顯然是一段右括號接上一段左括號)長什麼樣,那麼對於一個塊,我們顯然可以在 \(\mathcal O(\sqrt{n})\) 的時間內對其求出其進行重構,因此每次修改完都重構一遍複雜度是不會出現問題的。
接下來考慮怎樣查詢一個區間是否是合法的括號序列,對於此題而言比較噁心的一點是,當我們合併兩個塊 \(x,y\) 時,要對兩個塊中間的部分進行括號相消時暴力跑複雜度是 \(\sqrt{n}\) 地,再加上總共查詢可能會達到 \(\sqrt{n}\)
vector
,並用一個三元組 \((x,l,r)\) 描述一個連續段,表示這個連續段是第 \(x\)細節有一點點多,不過相信聰明的讀者們定能夠將每種情況都分析清楚(
const int MAXN=1e5; const int BLK=316; const u64 B=191; u64 pw[MAXN+5]; int n,k,qu,a[MAXN+5],blk_cnt,blk_sz; int bel[MAXN+5],L[BLK+5],R[BLK+5]; bool ok[BLK+5]; vector<int> lft[BLK+5],rit[BLK+5]; vector<u64> lft_hs[BLK+5];u64 rit_hs[BLK+5]; struct node{int x,l,r;u64 hs;}; u64 gethash(int x,int l,int r){ return (lft_hs[x][l]-((r+1==lft[x].size())?0:lft_hs[x][r+1]*pw[r-l+1])); } void redone(int x){ stack<int> stk;ok[x]=1;lft[x].clear();rit[x].clear(); for(int i=L[x];i<=R[x];i++){ if(a[i]<0){ if(!stk.empty()&&stk.top()^(-a[i])) return ok[x]=0,void(); else if(stk.empty()) rit[x].pb(-a[i]);else stk.pop(); } else stk.push(a[i]); } while(!stk.empty()) lft[x].pb(stk.top()),stk.pop(); lft_hs[x].resize(lft[x].size(),0);rit_hs[x]=0; for(int i=(int)(lft[x].size())-1;~i;i--) lft_hs[x][i]=((i+1==lft[x].size())?0:lft_hs[x][i+1])*B+lft[x][i]; for(int i=0;i<rit[x].size();i++) rit_hs[x]+=rit[x][i]*pw[i]; // printf("Block %d:\n",x); // for(int i=0;i<lft[x].size();i++) printf("%d ",lft[x][i]);printf("\n"); // for(int i=0;i<rit[x].size();i++) printf("%d ",rit[x][i]);printf("\n"); } bool query(int l,int r){ if(bel[l]==bel[r]){ stack<int> stk; for(int i=l;i<=r;i++){ if(a[i]<0){ if(stk.empty()||stk.top()^(-a[i])) return 0; stk.pop(); } else stk.push(a[i]); } return stk.empty(); } for(int i=bel[l]+1;i<bel[r];i++) if(!ok[i]) return 0; stack<node> stk; for(int i=l;i<=R[bel[l]];i++){ if(a[i]<0){ if(stk.empty()) return 0;node t=stk.top();stk.pop(); u64 hs=(!~t.l)?t.hs:gethash(t.x,t.l,t.l);if(hs+a[i]) return 0; if(t.l^t.r) stk.push({t.x,t.l+1,t.r,gethash(t.x,t.l+1,t.r)}); } else stk.push({bel[l],-1,-1,a[i]}); } for(int i=bel[l]+1;i<bel[r];i++){ int need=rit[i].size(),clen=0;u64 cur_hs=0; while(!stk.empty()&&need){ node t=stk.top();stk.pop();int len=t.r-t.l+1; if(need<=len){ if(!~t.l) cur_hs+=t.hs*pw[clen]; else cur_hs+=gethash(t.x,t.l,t.l+need-1)*pw[clen]; if(need^len) stk.push({t.x,t.l+need,t.r,gethash(t.x,t.l+need,t.r)}); need=0;break; } else { cur_hs+=t.hs*pw[clen]; need-=len;clen+=len; } } if(need) return 0;if(cur_hs^rit_hs[i]) return 0; if(!lft[i].empty()) stk.push({i,0,lft[i].size()-1,lft_hs[i][0]}); } for(int i=L[bel[r]];i<=r;i++){ if(a[i]<0){ if(stk.empty()) return 0;node t=stk.top();stk.pop(); u64 hs=(!~t.l)?t.hs:gethash(t.x,t.l,t.l);if(hs+a[i]) return 0; if(t.l^t.r) stk.push({t.x,t.l+1,t.r,gethash(t.x,t.l+1,t.r)}); } else stk.push({bel[l],-1,-1,a[i]}); } return stk.empty(); } int main(){ scanf("%d%d",&n,&k); for(int i=(pw[0]=1);i<=n;i++) pw[i]=pw[i-1]*B; blk_cnt=(int)pow(n,0.5);blk_sz=(n-1)/blk_cnt+1; for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=blk_cnt;i++){ L[i]=(i-1)*blk_sz+1;R[i]=min(i*blk_sz,n); // printf("%d %d\n",L[i],R[i]); for(int j=L[i];j<=R[i];j++) bel[j]=i; } for(int i=1;i<=blk_cnt;i++) redone(i); scanf("%d",&qu); while(qu--){ int opt;scanf("%d",&opt); if(opt==1){ int p,x;scanf("%d%d",&p,&x);a[p]=x; redone(bel[p]); } else { int l,r;scanf("%d%d",&l,&r); printf("%s\n",query(l,r)?"Yes":"No"); } } return 0; } /* 10 10 4 4 4 -4 1 -1 -4 8 -8 -4 1 2 2 9 10 1 9 3 7 -7 4 -4 -3 1 -1 -9 1 2 1 10 */