HDU1540 Tunnel Warfare(线段树:维护最大连续子串)

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;
}

你可能感兴趣的:(practice,again,数据结构--线段树,★★★,ACM--题解汇总,注意!,ACM算法竞赛入门经典题解)