【題解】「Ynoi2019」魔法少女網站 [*hard]
阿新 • • 發佈:2021-01-01
首先不難想到對題目進行一個轉化:對於詢問操作,其實就是以 \(>x\) 的位置為斷點,然後問剩下的區間貢獻和(形如 \(\sum\frac{len(len-1)}{2}\) 的貢獻式)。
\(>x\) 的這個斷點限制很不好辦,可不可以將所有的詢問按照 \(x\) 從小到大排序後依次處理呢?
那麼現在的任務就是維護一個 01
序列,最開始每個位置都是 0
。需要短時間內支援將 0
換成 1
,然後支援區間詢問。但是修改操作的話每一次都需要考慮,複雜度顯然錯誤。
考慮對操作分塊:每 \(\sqrt{n}\) 次重建原序列,然後暫時儲存的 \(\sqrt{n}\) 次操作一起做。
然後接下來就是維護這個 01
0
換成 1
的話,則對於每個極長的 1
區間,在其端點記錄資訊後,可以直接合並,然後每一個塊記錄一下貢獻和即可。但是如果是將 1
換成 0
的話,就需要拆開區間,但這顯然沒法 \(O(1)\) 完成。
容易發現將 1
換成 0
的操作只會在這 \(\sqrt{n}\) 個操作中出現。考慮先將有修改的位置空出來。然後每一次詢問時再將這些位置插入進去,記錄一下改變的答案量和指標個數(顯然最多兩個),詢問完了原地撤回即可。這樣子的話這些操作全部變成了將 0
換成 0
或 1
,就可以維護了。
關於實現:
- 本體程式碼量較大,很卡常,除錯難度中等偏低。
- 注意多用點
const
modify
的時候要多次訪問 \(pos-1\) 和 \(pos+1\) ,接個const
會快不少。 - 注意多調調塊長:因為常數,其實對操作分塊時塊大小遠大於 \(\sqrt{m}\)(經過多次嘗試我採用的是 \(\sqrt{\frac{27}{4}m}\) 的塊大小),對於序列分塊的話也要注意常數,這裡主要其實是詢問時的常數(這裡採用的 \(\sqrt{\frac{4}{5}n}\) 的塊大小)。
(感覺這系列題目序列分塊的塊大小都最好小於 \(\sqrt{n}\) ,就前幾題而言,塊大小在 \([\sqrt{0.7n},\sqrt{0.8n}]\) 之間跑的很快。
const int N=3e5+5; const int S=2e3+5; ll calc[N]; int n,m,c1,c2,a[N]; struct Modify {int tim,pos,val;} q1[S]; struct Query {int tim,l,r,lim,id;} q2[S]; // {{{ Data_Struct_Block bool seq[N]; int L[S],R[S],id[N],p[N],sum[S]; inline void clear() {CLEAR(seq),CLEAR(p),CLEAR(sum);} inline void init() { int sqrtn=sqrt(n*4/5+1); for(int i=1,c=1,j;i<=n;i=j+1,++c) { L[c]=i,R[c]=j=min(n,i+sqrtn); lep(t,L[c],R[c]) id[t]=c; } } // {{{ modify int top; struct Node { int id,ans,t1[2],t2[2]; bool typ; inline void clear() {t1[0]=t2[0]=t1[1]=t2[1]=typ=0;} } sta[S]; Node tmp_node; inline void modify(const int &pos,bool typ) { p[pos]=pos,seq[pos]=1; tmp_node.id=pos; const int las=pos-1,nxt=pos+1,tmp_p=p[las]; const bool able1=(seq[las]&&L[id[pos]]!=pos),able2=(seq[nxt]&&R[id[pos]]!=pos); if(!able1&&!able2) tmp_node.clear(),tmp_node.ans=1; else { tmp_node.typ=1; if(able1&&able2) { tmp_node.ans=(nxt-p[las])*(p[nxt]-las); tmp_node.t1[0]=p[las],tmp_node.t1[1]=p[p[las]],p[p[las]]=p[nxt], tmp_node.t2[0]=p[nxt],tmp_node.t2[1]=p[p[nxt]],p[p[nxt]]=tmp_p; } else { if(able1) { tmp_node.ans=nxt-p[las]; tmp_node.t1[0]=pos,tmp_node.t1[1]=p[pos],p[pos]=p[las], tmp_node.t2[0]=p[las],tmp_node.t2[1]=p[p[las]],p[p[las]]=pos; } else { tmp_node.ans=p[nxt]-las; tmp_node.t1[0]=pos,tmp_node.t1[1]=p[pos],p[pos]=p[nxt], tmp_node.t2[0]=p[nxt],tmp_node.t2[1]=p[p[nxt]],p[p[nxt]]=pos; } } } sum[id[pos]]+=tmp_node.ans; if(typ) sta[++top]=tmp_node; } inline void back() { while(top) { tmp_node=sta[top]; --top; sum[id[tmp_node.id]]-=tmp_node.ans,seq[tmp_node.id]=0; if(tmp_node.typ==1) p[tmp_node.t2[0]]=tmp_node.t2[1],p[tmp_node.t1[0]]=tmp_node.t1[1]; } } // }}} inline ll query(const int &l,const int &r) { ll ans=0; if(id[l]==id[r]) { int cnt=0; lep(i,l,r) {if(seq[i]) ++cnt; else ans+=calc[cnt],cnt=0;} return ans+calc[cnt]; } int c1=0,c2=0; lep(i,l,R[id[l]]) {if(seq[i]) ++c1; else ans+=calc[c1],c1=0;} rep(i,r,L[id[r]]) {if(seq[i]) ++c2; else ans+=calc[c2],c2=0;} int tot=c1; lep(i,id[l]+1,id[r]-1) { if(p[L[i]]==R[i]) tot+=R[i]-L[i]+1; else { if(seq[L[i]]) tot+=p[L[i]]-L[i]+1,ans-=calc[p[L[i]]-L[i]+1]; ans+=calc[tot]+sum[i],tot=0; if(seq[R[i]]) tot+=R[i]-p[R[i]]+1,ans-=calc[R[i]-p[R[i]]+1]; } } return ans+calc[tot+c2]; } // }}} // {{{ solve ll ans[S]; int head[N],cnt; struct Edge {int nxt,to;} G[N]; inline void addedge(int u,int v) {G[++cnt]=(Edge){head[u],v},head[u]=cnt;} bool broke[N],use[N]; inline void solve() { clear(); lep(i,1,c1) broke[q1[i].pos]=true; lep(i,1,n) if(!broke[i]) addedge(a[i],i); std::sort(q2+1,q2+1+c2,[](Query x,Query y){return x.lim<y.lim;}); int pot=1; lep(i,1,c2) { while(pot<=q2[i].lim) {for(int &e=head[pot];e;e=G[e].nxt) modify(G[e].to,0); ++pot;} rep(j,c1,1) if(q1[j].tim<q2[i].tim&&!use[q1[j].pos]) { use[q1[j].pos]=true; if(q1[j].val<=q2[i].lim) modify(q1[j].pos,1); } lep(j,1,c1) if(!use[q1[j].pos]) { use[q1[j].pos]=true; if(a[q1[j].pos]<=q2[i].lim) modify(q1[j].pos,1); } ans[q2[i].id]=query(q2[i].l,q2[i].r); back(); lep(j,1,c1) use[q1[j].pos]=false; } CLEAR(head),cnt=0; lep(i,1,c1) broke[q1[i].pos]=false; } // }}} int sqrtm,op,l,r,x,y; int main() { IN(n,m),sqrtm=sqrt(m*27/4+1),init(); lep(i,1,n) calc[i]=1ll*i*(i+1)/2; lep(i,1,n) IN(a[i]); for(int i=1,j;i<=m;i=j+1) { j=min(m,i+sqrtm),c1=c2=0; lep(t,i,j) { IN(op); if(op==1) IN(x,y),q1[++c1]=(Modify){t,x,y}; if(op==2) IN(l,r,x),q2[++c2]=(Query){t,l,r,x,c2}; } solve(); lep(i,1,c2) printf("%lld\n",ans[i]); lep(i,1,c1) a[q1[i].pos]=q1[i].val; } return 0; }