HDU1540線段樹維護連續子區間
阿新 • • 發佈:2020-11-06
------------恢復內容開始------------
感謝大佬的部落格,受益匪淺
https://blog.csdn.net/weixin_42469716/article/details/102938021?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160431756819195264713806%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=160431756819195264713806&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-1-102938021.pc_first_rank_v2_rank_v28p&utm_term=hdu1540&spm=1018.2118.3001.4449
題目大意:
1-n個地道,m個次操作,D代表摧毀第i個地道,Q代表查詢包含第i個地道的最大連續地道數目,並輸出。R代表修復最近摧毀的那個地道
解題思路:
R表示修復最近摧毀的那個通道,所以自然而然的想利用棧來解決,Q代表查詢包含第i個地道的最大連續地道數目,這個就表示要求求出包含在本節點在內的最大連續區間的和。這裡要用到求子區間和的方法,
求最大連續區間的和的方法:維護區間中從左節點開始的最大連續區間和區間中從右節點開始的最大連續區間和,以及在這個區間中最大連續區間和
void up(int p){ //計算左端開始的連續和 t[p].ls=t[p<<1].ls;if(t[p<<1].ls==t[p<<1].r-t[p<<1].l+1)t[p].ls+=t[p<<1|1].ls; //計算右端開始的連續和 t[p].rs=t[p<<1|1].rs; if(t[p<<1|1].rs==t[p<<1|1].r-t[p<<1|1].l+1)t[p].rs+=t[p<<1].rs; //計算最大連續和 t[p].m=max(t[p<<1].rs+t[p<<1|1].ls,max(t[p<<1].m,t[p<<1|1].m));}
剩下的就是單點修改,單點查詢的線段樹板子
1 #include <cstdio> 2 #include <iostream> 3 #include <cmath> 4 #include <iomanip> 5 #include <string.h> 6 #include <cstring> 7 #include <algorithm> 8 #include <vector> 9 #include <map> 10 #include <stack> 11 #include <utility> 12 using namespace std; 13 typedef long long ll ; 14 typedef unsigned long long ull ; 15 const int N=1e5+90; 16 const int inf=1e9+90; 17 #define eps 1e-10 18 #define forn(i,n) for(int i=0;i<n;i++) 19 #define form(i,n) for(int i=1;i<=n;i++) 20 ll a[N]; 21 int n,m; 22 char c[4]; 23 struct Node{ 24 int l,r; 25 ll ls,rs,m; 26 }t[N<<2]; 27 void up(int p){ 28 t[p].ls=t[p<<1].ls; 29 if(t[p<<1].ls==t[p<<1].r-t[p<<1].l+1)t[p].ls+=t[p<<1|1].ls; 30 t[p].rs=t[p<<1|1].rs; 31 if(t[p<<1|1].rs==t[p<<1|1].r-t[p<<1|1].l+1)t[p].rs+=t[p<<1].rs; 32 t[p].m=max(t[p<<1].rs+t[p<<1|1].ls,max(t[p<<1].m,t[p<<1|1].m)); 33 } 34 void build(int l,int r,int p){ 35 if(l==r){ 36 t[p]={l,r,1,1,1}; 37 return; 38 } 39 t[p]={l,r,0,0,0}; 40 int mid=l+r>>1; 41 if(l<=mid)build(l,mid,p<<1); 42 if(r>mid)build(mid+1,r,p<<1|1); 43 t[p].ls=t[p].rs=t[p].m=t[p<<1].m+t[p<<1|1].m; 44 } 45 void update(int l,int c,int p){ 46 if(t[p].l==t[p].r){ 47 t[p].ls=t[p].rs=t[p].m=c; 48 //到達葉節點更新資料 49 return; 50 } 51 int mid=t[p].l+t[p].r>>1; 52 if(l<=mid)update(l,c,p<<1); 53 if(l>mid)update(l,c,p<<1|1); 54 up(p); 55 } 56 ll ask(int l,int p){ 57 if(t[p].l==t[p].r||t[p].r-t[p].l+1==t[p].m||t[p].m==0){ 58 return t[p].m;//如果到達葉子結點,或者這是一段完全連續的區間,或者這裡被炸完了直接返回 59 } 60 int mid=t[p].l+t[p].r>>1; 61 ll sum=0; 62 if(l<=mid){ 63 if(t[p<<1].r-(t[p<<1].rs-1)<=l)sum=t[p<<1].rs+ask(mid+1,p<<1|1); 64 //即查詢的坑道在左邊,且左段從右開始的連續坑道能覆蓋查詢坑道,這時直接加上t[p].rs,加上以mid+1作為目標點搜右半段的長度 65 else sum=ask(l,p<<1); 66 //否則目標坑道不變搜左段 67 }else if(l>mid){ 68 if(t[p<<1|1].l+t[p<<1|1].ls-1>=l)sum=t[p<<1|1].ls+ask(mid,p<<1); 69 //即查詢的坑道在右邊,且右段從左開始的連續坑道能覆蓋查詢坑道,這時直接加上t[p].ls,加上以mid作為目標點搜左半段的長度 70 else sum=ask(l,p<<1|1); 71 //否則目標坑道不變搜右段 72 } 73 return sum; 74 } 75 stack<int>sk; 76 int main(){ 77 // freopen("in.txt","r",stdin); 78 // freopen("out.txt","w",stdout); 79 while (~scanf("%d%d",&n,&m)) { 80 int x; 81 while (!sk.empty())sk.pop(); 82 build(1, n, 1); 83 for (int i = 0; i < m; i++) { 84 scanf("%s", c); 85 if (c[0] == 'D') { 86 scanf("%d", &x); 87 sk.push(x); 88 update(x,0, 1); 89 } else if (c[0] == 'Q') { 90 scanf("%d", &x); 91 printf("%lld\n", ask(x, 1)); 92 } else { 93 int tmp = sk.top(); 94 update(tmp,1, 1); 95 sk.pop(); 96 } 97 } 98 } 99 }