cogs265.線段覆蓋
265. 線段覆蓋
★★★☆ 輸入文件:xdfg.in
輸出文件:xdfg.out
簡單對比
時間限制:2 s
內存限制:20 MB
【問題描述】
有一根長度為 L 的白色條狀物。有兩種操作:
- 用一條長度為 T 的黑布蓋住條狀物的 [a, a+T] 這個區間 (0<=a, T<=L) 。
- 把某條黑布拿走。
輸入 L 和 n 次操作,要你輸出每次操作之後:
- 條狀物上有多少個黑區間。
- 條狀物上黑區間的總長度。
【輸入格式】
輸入文件第一行兩個整數L(1<=L<=200000), n(1<=n<=200000)
以下有n行,第2--n+1行每行有3個整數m,a,T,m表示操作類型,1表示放入黑布,2表示拿走黑布,a,T表示黑布在L上的起始位置與長度,拿走的黑布保證是原來已經存在的.
【輸出格式】
輸出有n行,每行兩個整數x,y,x表示L上的黑區間個數,y表示黑區間的總長度.
【輸入輸出樣例】
輸入:
20 4
1 5 3
1 7 2
2 5 3
1 16 3
輸出:
1 3
1 4
1 2
2 5
[題解]
線段樹維護區間被覆蓋長度 段數 被覆蓋層數 "左融合" "右融合";由此可以實現每次O(1)查詢;
解釋:所謂左右融合其實就是判斷用的,左右融合就是字面意思,能否與左邊右邊的黑布融合,用於數據上傳;
本題難在兩點:
1.數據上傳的時候,通過"左融合" "右融合"現實處理計算區間段數:
由兒子上傳數據時,當前區間的段數等於左兒子的段數加右兒子的段數,
難點在於:如果左兒子最右邊是被黑布蓋著,右兒子左邊是被黑布蓋著,顯然段數多算了1,
而左右融合此時的作用體現了,若左兒子右融合等於1,右兒子左融合等於1,那麽此節點段數需要減1,正確性,,很顯然啊....
2.(我之前就一直卡在這裏)
本來我想用lazy下傳黑布,後來發現不可做...必須每次操作具體到葉子,於是T了很多點,
後來發現這個題有個特點, 撤走的黑布一定存在!
那麽就不需要lazy下傳了,只需要維護區間被蓋了幾次
撤走黑布就把蓋的次數-1;
當被覆蓋0次那麽所有數據清空;
否則保留.
於是,A了.
[代碼]
#include <cstdio> #include <algorithm> usingnamespace std; int n,t; struct d { int len,s; int zr,yr; int c; }; d node[400000]; inline void gengxin(int o,int l,int r) { if(node[o].c>0) { node[o].zr=node[o].yr=node[o].s=1; node[o].len=r-l+1; return; } else { node[o].zr=node[o*2].zr; node[o].yr=node[o*2+1].yr; node[o].len=node[o*2].len+node[o*2+1].len; node[o].s=node[o*2].s+node[o*2+1].s; if(node[o*2].yr==1&&node[o*2+1].zr==1)node[o].s--; return ; } } inline void add(int o,int l,int r,int nl,int nr,int v) { if(l>=nl&&r<=nr) { node[o].c+=v; if(l==r) { if(node[o].c>0) { node[o].len=node[o].zr=node[o].yr=node[o].s=1; return; } else { node[o].len=node[o].zr=node[o].yr=node[o].s=0; return; } } else { gengxin(o,l,r); return; } } int m=(l+r)>>1; if(m>=nl) { add(o*2,l,m,nl,nr,v); } if(m<nr) { add(o*2+1,m+1,r,nl,nr,v); } gengxin(o,l,r); } int main() { freopen("xdfg.in","r",stdin); freopen("xdfg.out","w",stdout); scanf("%d%d",&n,&t); while(t--) { int x,y,z; scanf("%d%d%d",&x,&y,&z); if(x==1) { add(1,1,n,y,y+z-1,1); } else { add(1,1,n,y,y+z-1,-1); } printf("%d %d\n",node[1].s,node[1].len); } return 0; }
cogs265.線段覆蓋