HDU-1540 Tunnel Warfare

所有村庄排成一条直线,相邻的两两连通,如果一个村庄被摧毁,左右两个区间就不连通了

要查询一个村庄能连通的村庄的数量(包括自己),可以改为查询离它最近的两个被摧毁的村庄,这样相减就是答案

所以用线段树,标记每个区间是否有断点,查询时只查找有断点的区间,还可以优化剪枝

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<stack>

using namespace std;
typedef long long ll;
const int maxn=50000+10; //数组大小

bool vis[maxn<<2]; //标记此区间是否有断点

void PushUp(int rt) //向上更新父节点
{
    vis[rt]=vis[rt<<1]||vis[rt<<1|1];
}

void build(int l,int r,int rt) //建树
{
    if(l==r)
    {
        vis[rt]=false;
        return;
    }
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    PushUp(rt);
}

void update(int p,bool f,int l,int r,int rt) //将p点更新为f状态
{
    if(l==r)
    {
        vis[rt]=f;
        return;
    }
    int m=(l+r)>>1;
    if(p<=m) update(p,f,l,m,rt<<1);
    else update(p,f,m+1,r,rt<<1|1);
    PushUp(rt);
}
int ml,mr; //左右断点
void query(int p,int l,int r,int rt) //查询离p点最近的两个断点
{
    if(!vis[rt]) return; //没有断点
    if(l==r)
    {
        if(l<=p)
        ml=max(ml,l);
        if(r>=p)
        mr=min(mr,r);
        return;
    }
    if(r<=ml||l>=mr) return; //剪枝
    int m=(l+r)>>1;
    if(p<=m) //p在左,优先左子树
    {
        query(p,l,m,rt<<1);
        query(p,m+1,r,rt<<1|1);
    }
    else //优先右子树
    {
        query(p,m+1,r,rt<<1|1);
        query(p,l,m,rt<<1);
    }
}
int n,m,p;
char c[5];
int main()
{
    //freopen("/home/zlwang/test.txt","r",stdin);
    while(~scanf("%d%d",&n,&m))
    {
        build(1,n,1);
        stack<int> s; //用栈保存被摧毁的村庄
        for(int i=0;i<m;i++)
        {
            scanf("%s",c);
            if(c[0]=='Q')
            {
                scanf("%d",&p);
                ml=0;
                mr=n+1;
                query(p,1,n,1);
                //cout<<ml<<" "<<mr<<endl;
                int ans=mr-ml-1;
                if(ans<0) ans=0;
                printf("%d\n",ans);
            }
            else if(c[0]=='D')
            {
                scanf("%d",&p);
                update(p,true,1,n,1); //摧毁
                s.push(p);
            }
            else if(c[0]=='R')
            {
                p=s.top();
                s.pop();
                update(p,false,1,n,1); //恢复
            }
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值