HDU1540线段树维护连续子区间
Posted ilikeeatfish
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU1540线段树维护连续子区间相关的知识,希望对你有一定的参考价值。
------------恢复内容开始------------
感谢大佬的博客,受益匪浅
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 ", ask(x, 1)); 92 } else { 93 int tmp = sk.top(); 94 update(tmp,1, 1); 95 sk.pop(); 96 } 97 } 98 } 99 }
以上是关于HDU1540线段树维护连续子区间的主要内容,如果未能解决你的问题,请参考以下文章
HDU 1540 Tunnel Warfare (线段树或set水过)
HDU 1540 Tunnel Warfare 线段树区间合并