HDU1540 Tunnel Warfare
该题目有点意思,不容易想到是线段树,但是用线段树还有2种方法(我了解到的),这里选一种稍稍麻烦的办法说一下。
题目中有三个操作,一个是毁坏一个城市,一个是查询,一个是修复所有的城市,询问则是询问该城市左右相连的城市最大数量(毁掉就不算相连了)。
由于长度的限制,n^2复杂度舍弃了,至少是nlogn也就是今天要说的线段树。
那么线段树节点存什么呢?
除了该节点的l,r以外(此题推荐将l,r写在结构体里,虽然本人习惯都是将l,r作为函数的参数不断传参实现)
还有三个区间
ls:区间内的最左区间
ms:区间内的最大区间
rs:区间内的最右区间
查询时除了注意落在对应区间内还要住注意左右区间的合并,对于更新操作也是如此。具体什么意思,代码注释写的很详细了。
代码如下:
#include<iostream> #include<algorithm> #include<string> #include<string.h> #include<cmath> #include<stdio.h> using namespace std; #define zuo root*2 #define you root*2+1 #define INF 0x3f3f3f3f //#pragma warning(disable:4996) int n,m,x,q[50010];//如果修复多个用q模拟队列存一下 int max(int a,int b) { if(a>b) return a; return b; } struct node { int l,r; int ls,rs,ms; }t[200050]; void build(int root,int l,int r) { t[root].l=l;t[root].r=r; t[root].ls = t[root].rs = t[root].ms = r-l+1; if(l!=r) { int mid = (l + r)>>1; build(zuo, l, mid); build(you, mid + 1, r); } } void update(int root, int seat, int kind) { if (t[root].l == t[root].r) { if(kind==1)//修复 t[root].ls = t[root].rs = t[root].ms = 1; else t[root].ls = t[root].rs = t[root].ms = 0; return; } //pushdown(root, r - l + 1); int mid = (t[root].l + t[root].r)>>1; if(seat<=mid) update(zuo,seat, kind); else update(you,seat, kind); t[root].ls=t[zuo].ls; if(t[zuo].ls == t[zuo].r-t[zuo].l+1)//左子树区间满的特殊情况 t[root].ls += t[you].ls; t[root].rs=t[you].rs; if(t[you].rs == t[you].r-t[you].l+1) t[root].rs += t[zuo].rs; t[root].ms=max(max(t[zuo].ms,t[you].ms),t[zuo].rs+t[you].ls);// 最大值取三个值中的最大值 } int query(int root, int seat) { if(t[root].l == t[root].r || t[root].ms == 0 || t[root].ms == t[root].r-t[root].l+1)//到了叶节点或者该区间为空或满都不必继续走 return t[root].ms;//体现了ms损坏时赋值0的妙处 int mid = (t[root].l+t[root].r)>>1; if(seat<=mid) { if(seat >= t[zuo].r - t[zuo].rs + 1)//如果seat在左子树的右区间,则要加上右子树的左区间,思想同更新的时候 return query(zuo,seat)+query(you,mid+1); else return query(zuo,seat); } else { if(seat<=t[you].l+t[you].ls-1)//同理 return query(zuo,mid)+query(you,seat); else return query(you,seat); } } //询问的话要在父节点就决定子节点的询问,即在父节点就要知道自己点的相关信息 //所以我第一次发现我的模板的不好处,此处是这样子写的,下面注释附上我原来的模板写法 (模板里面是加法的线段树) /* int query(int root, int l, int r, int ql, int qr) { if (l > qr || r < ql) return 0; pushdown(root, r - l + 1); if (l >= ql&& r <= qr) return t[root].val; int mid = (l + r) / 2; return query(zuo, l, mid, ql, qr) + query(you, mid + 1, r, ql, qr); }*/ int main() { char s[2]; while(~scanf("%d%d",&n,&m)) { int top = 0; build(1,1,n); while(m--) { scanf("%s",s); if(s[0] == 'D') { scanf("%d",&x); q[top++] = x; update(1,x,0); } else if(s[0] == 'Q') { scanf("%d",&x); printf("%d\n",query(1,x)); } else { if(x>0) { x=q[--top]; update(1,x,1); } } } } return 0; }