HDU1540 Tunnel Warfare(线段树:维护最大连续子串)
http://acm.hdu.edu.cn/showproblem.php?pid=1540
分析:
首先先来分析题目中的3种操作:
1.D x: 该操作就是单点更新
2.Q x: 该操作可以分解为查区间[1,x]的最大连续0后缀长L和区间[x,n]的最大连续0前缀长R,则R+L-1即为所求.有关前缀查询和后缀查询可以参考:
http://blog.csdn.net/u013480600/article/details/22421981
3.R : 该操作其实就是update,不过需要一个stack来保存以前D过的点.
维护一棵线段树,该树的每个节点维护的信息为:
cover:-1,0或1,分别表示多重信息,未被摧毁,被摧毁
pre: 表示当前节点的最大连续0的前缀长度
suf: 表示当前节点的最大连续0的后缀长度
线段树的基本操作.
1. PushUp(i,l,r):根据子节点的信息更新父节点的cover,suf,pre信息
2. PushDown(i,l,r):如果cover位!=-1,则更新子树的3类信息
3. build(i,l,r):建树
如果l==r,单独处理并返回.
否则先PushDown;
递归建立左右子树
PushUp
4. update(p,v,i,l,r):将p位置的cover值置为v.
如果l==r,那么直接更新信息并返回.
否则先PushDown.
如果p<=m,就update左子树
否则update 右子树
最后PushUp
5. query_pre(ql,qr,i,l,r):查询[ql,qr]与[l,r]区间公共部分的最长前缀连续0长度.
if(ql<=l && r<=qr) 直接返回 pre[i]
PushDown
如果[ql,qr]只与[l,r]的左子树相关,就return query_pre(lson)
如果[ql,qr]只与[l,r]的右子树相关,就return query_pre(rson)
左右子树都相关,则令L= query_pre(lson),如果L==m-max(ql,l)+1
,那么就令L+=query_pre(rson)
返回L
6. query_suf(ql,qr,i,l,r):查询[ql,qr]与[l,r]区间公共部分的最长后缀连续0长度.
if(ql<=l && r<=qr) 直接返回 suf[i]
PushDown
如果[ql,qr]只与[l,r]的左子树相关,就return query_suf(lson)
如果[ql,qr]只与[l,r]的右子树相关,就return query_suf(rson)
左右子树都相关,则令R= query_suf(rson),如果R==min(qr,r)-m
,那么就令R+=query_suf(lson)
返回L
AC代码:453ms,一次编译通过并AC
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=50000+1000;
#define lson i*2,l,m
#define rson i*2+1,m+1,r
#define root 1,1,n
int cover[MAXN*4],pre[MAXN*4],suf[MAXN*4];
void PushUp(int i,int l,int r)
{
int m=(l+r)/2;
//cover
if(cover[i*2]==-1 || cover[i*2+1]==-1)
cover[i]=-1;
else if(cover[i*2] != cover[i*2+1])
cover[i]=-1;
else
cover[i]=cover[i*2];
//pre
pre[i]=pre[i*2];
if(pre[i]== m-l+1)pre[i] +=pre[i*2+1];
//suf
suf[i]=suf[i*2+1];
if(suf[i]==r-m) suf[i]+=suf[i*2];
}
void PushDown(int i,int l,int r)
{
int m=(l+r)/2;
if(cover[i]!=-1)
{
cover[i*2]=cover[i*2+1]=cover[i];
suf[i*2]=pre[i*2]= (cover[i]?0:m-l+1);
suf[i*2+1]=pre[i*2+1]= (cover[i]?0:r-m);
}
}
void build(int i,int l,int r)
{
if(l==r)
{
cover[i]=0;
suf[i]=pre[i]=1;
return ;
}
int m=(l+r)/2;
build(lson);
build(rson);
PushUp(i,l,r);
}
void update(int p,int v,int i,int l,int r)
{
if(l==r)
{
cover[i]=v;
suf[i]=pre[i]= (v?0:1);
return ;
}
PushDown(i,l,r);
int m=(l+r)/2;
if(p<=m) update(p,v,lson);
else update(p,v,rson);
PushUp(i,l,r);
}
int query_pre(int ql,int qr,int i,int l,int r)//查找[ql,qr]与[l,r]的公共部分的最大前缀连续0的长度
{
if(ql<=l && r<=qr)
return pre[i];
PushDown(i,l,r);
int m=(l+r)/2;
if(qr<=m) return query_pre(ql,qr,lson);
if(m sq;
while(m--)
{
char str[10];
int x;
scanf("%s",str);
if(str[0]=='D')
{
scanf("%d",&x);
sq.push(x);
update(x,1,root);
}
else if(str[0]=='Q')
{
scanf("%d",&x);
int L=query_suf(1,x,root);
int R=query_pre(x,n,root);
if(L==0)
printf("0\n");
else
printf("%d\n",L+R-1);
}
else if(str[0]=='R')
{
if(!sq.empty())
{
int x= sq.top();sq.pop();
update(x,0,root);
}
}
}
}
return 0;
}