CF915E Physical Education Lessons|動態開點線段樹
阿新 • • 發佈:2018-11-24
physical swa oid else 動態 減少 add 會有 有用
動態開點線段樹
題目暗示了區間修改,所以我們自然想到了用線段樹來維護非工作日的天數。
然而我們再看一下數據範圍,天數n的範圍是\(1 \le n \le 10^9\),像普通線段樹一樣預處理顯然會爆空間。
天無絕人之路,我們看一下修改個數,$1\le q \le 3 \cdot 10^5 $, 比天數少很多,這也意味著,我們預先處理好的線段樹有些節點並沒有用
能否優化呢?答案是肯定的,這就是動態開點線段樹,顧名思義,我們只要到用某個節點的時候,才分配一個點給它,這樣使得我們使用的空間大大減少。其余的與普通線段樹大致是相同的。
最後再說一下,本題的數組要開大一些,否則會瘋狂\(RE\)
代碼奉上~
#include<bits/stdc++.h> using namespace std; int tree[15001000],lson[15001000],rson[15001000],tag[15001000],d; long long n,q,l,r,k; void freelazy(int x,int l,int r) { int mid=(l+r)/2; if (tree[x]) { tree[lson[x]]=(mid-l+1); tree[rson[x]]=(r-(mid+1)+1); } else { tree[lson[x]]=0; tree[rson[x]]=0; } tag[lson[x]]=1; tag[rson[x]]=1; tag[x]=0; //正常的懶標記下放操作 } void add(int x,int l,int r,int ql,int qr,int sd) { if (ql<=l&&qr>=r) { tree[x]=(r-l+1)*sd;tag[x]=1; return ; } if (!lson[x]) { d++; lson[x]=d; } if (!rson[x]) { d++; rson[x]=d; }//要用就開點 //上面要記得記錄左右兒子 if (tag[x]) freelazy(x,l,r); int mid=(l+r)/2; if (ql>mid) { add(rson[x],mid+1,r,ql,qr,sd); } else if (qr<=mid) { add(lson[x],l,mid,ql,qr,sd); } else { add(lson[x],l,mid,ql,mid,sd); add(rson[x],mid+1,r,mid+1,qr,sd); } tree[x]=tree[lson[x]]+tree[rson[x]]; //正常的線段樹操作 } int main() { cin>>n>>q;d=1; for (int i=1;i<=q;i++) { scanf("%d%d%d",&l,&r,&k);//本題用cin、cout會有超時風險 if (l>r) swap(l,r); if (k==1) { add(1,1,n,l,r,k); } else { add(1,1,n,l,r,0); } printf("%d\n",n-tree[1]); } return 0; }
CF915E Physical Education Lessons|動態開點線段樹