hdu1540&poj2892Tunnel Warfare(线段树单点更新)

->题目请戳这 和这->

题目大意,给n个点表示n个村庄,一开始都是相连的,现在有3种操作:Q x,查询第与第x个村庄相连的村庄个数;D x,摧毁掉第x个村庄;R  恢复刚摧毁的村庄。

题目分析:每个点用2个状态表示,0表示被摧毁,1表示存在,因为有恢复操作,并且每次恢复上一个被摧毁的村庄,所以用一个栈存储所有被摧毁的村庄,每次R操作恢复栈顶村庄。这题关键是查询操作,如果查询的村庄x不存在,则没有村庄与之相连通,如果存在x村庄,那么从x往2边找连续的存在的村庄。如何找这个连续的存在的村庄,这里提供2种方法:

1。自顶向下找,如果找到左边第一个不存在的村庄,找到右边第一个不存在的村庄,相减即可。

2。自底向上找,我们建立线段树的时候把每个叶子节点即每个村庄的位置找到,然后分别从x-1和x+1的位置往两边找,也是找到第一个不存在的村庄为止。

本来比较看好第二种方法,实际提交了一下,第一种效率貌似高一点。

详情请见代码:

自顶向下:

#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 50005;
int stack[N],top;
int n,m;
int tree[N<<2];
char op[3];
int leaf[N];

void build(int num,int s,int e)
{
    if(s == e)
    {
        tree[num] = 1;
        leaf[s] = num;
        return;
    }
    int mid = (s + e)>>1;
    build(num<<1,s,mid);
    build(num<<1|1,mid + 1,e);
    tree[num] = tree[num<<1] + tree[num<<1|1];
}

void insert(int num,int s,int e,int pos,int val)
{
    if(s == e)
    {
        tree[num] = val;
        return;
    }
    int mid = (s + e)>>1;
    if(pos <= mid)
        insert(num<<1,s,mid,pos,val);
    else
        insert(num<<1|1,mid + 1,e,pos,val);
    if(tree[num<<1] == mid - s + 1 && tree[num<<1|1] == e - mid)
        tree[num] = e - s + 1;
    else
        tree[num] = 0;
}

int query(int num,int s,int e,int pos,int dir)
{
    if(pos > n || pos < 1)
        return pos;
    if(tree[num] == e - s + 1)
    {
        if(dir)//->
            return query(1,1,n,e + 1,dir);//重新从e+1开始往右找
        else
            return query(1,1,n,s - 1,dir);
    }
    if(s == e)
        return s;
    int mid = (s + e)>>1;
    if(pos <= mid)
        return query(num<<1,s,mid,pos,dir);
    else
        return query(num<<1|1,mid + 1,e,pos,dir);
}

void print(int num,int s,int e)
{
    for(int i = 1;i <= n;i ++)
        printf("%d ",tree[leaf[i]]);
}

int main()
{
    int x;
    int cnt;
    while(scanf("%d%d",&n,&m) != EOF)
    {
        build(1,1,n);
        top = 0;
        while(m --)
        {
            scanf("%s",op);
            if(op[0] == 'R')
            {
                insert(1,1,n,stack[--top],1);
            }
            else
            {
                scanf("%d",&x);
                if(op[0] == 'D')
                {
                    insert(1,1,n,x,0);
                    stack[top ++] = x;
                }
                else
                {
                    int ans = tree[leaf[x]];
                    if(ans == 0)
                        printf("0\n");
                    else
                    {
                        ans = 1;
                        if(x < n)
                        {
                            cnt = query(1,1,n,x + 1,1);//右
                            ans += (cnt - x - 1);
                        }
                        //printf("cnt:%d\n",cnt);
                        if(x > 1)
                        {
                            cnt = query(1,1,n,x - 1,0);//左
                            ans += (x - cnt - 1);
                        }
                        //printf("cnt:%d\n",cnt);
                        printf("%d\n",ans);
                    }
                }
            }
        }
    }
    return 0;
}
//hdu546MS	1008K
//poj1036K	266MS
自底向上找:

#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 50005;
int stack[N],top;
int n,m;
struct node
{
    int l,r,len;
}tree[N<<2];

char op[3];
int leaf[N];

void build(int num,int s,int e)
{
    if(s == e)
    {
        tree[num].len = 1;
        tree[num].l = tree[num].r = s;
        leaf[s] = num;
        return;
    }
    int mid = (s + e)>>1;
    build(num<<1,s,mid);
    build(num<<1|1,mid + 1,e);
    tree[num].len = tree[num<<1].len + tree[num<<1|1].len;
    tree[num].l = s;
    tree[num].r = e;
}

void insert(int num,int pos,int val)
{
    if(tree[num].l == tree[num].r)
    {
        tree[num].len = val;
        return;
    }
    int mid = (tree[num].l + tree[num].r)>>1;
    if(pos <= mid)
        insert(num<<1,pos,val);
    else
        insert(num<<1|1,pos,val);
    if(tree[num<<1].len == mid - tree[num].l + 1 && tree[num<<1|1].len == tree[num].r - mid)
        tree[num].len = tree[num].r - tree[num].l + 1;
    else
        tree[num].len = 0;
}

void print()
{
    int i;
    for(i = 1;i <= n;i ++)
        printf("%d ",tree[leaf[i]].len);
    putchar(10);
}

int main()
{
    int x;
    int cnt;
    while(scanf("%d%d",&n,&m) != EOF)
    {
        build(1,1,n);
        top = 0;
        while(m --)
        {
            scanf("%s",op);
            if(op[0] == 'R')
            {
                insert(1,stack[--top],1);
            }
            else
            {
                scanf("%d",&x);
                if(op[0] == 'D')
                {
                    insert(1,x,0);
                    stack[top ++] = x;
                }
                else
                {
                    int ans = tree[leaf[x]].len;
                    if(ans == 0)
                        printf("0\n");
                    else
                    {
                        int tl,tr;
                        tl = x - 1;//记录x左边第一个断点
                        tr = x + 1;//记录x右边第一个断点
                        int tmp;
                        while(tl >= 1 && tree[leaf[tl]].len == 1)
                        {
                            tmp = leaf[tl];
                            if(tmp & 1)//在右子树上,往根找就是往左找
                            {
                                while(tree[tmp].len == tree[tmp].r - tree[tmp].l + 1 && tmp > 1)
                                {
                                    tmp /= 2;
                                    if(!(tmp & 1))//到了左子树上,找到头了
                                    {
                                        break;
                                    }
                                }
                                if(tree[tmp].len == tree[tmp].r - tree[tmp].l + 1)//加上这句优化后hdu515MS	2032K
                                    tl = tree[tmp].l - 1;//poj2068K	282MS
                                else
                                    tl = tree[tmp<<1|1].l - 1;
                            }
                            else
                                tl --;
                        }
                        while(tr <= n && tree[leaf[tr]].len == 1)
                        {
                            tmp = leaf[tr];
                            if(tmp & 1)
                                tr ++;
                            else
                            {
                                while(tree[tmp].len == tree[tmp].r - tree[tmp].l + 1 && tmp > 1)
                                {
                                    tmp /= 2;
                                    if(tmp & 1)//找到了右子树上面,找到最右边了
                                        break;
                                }
                                if(tree[tmp].len == tree[tmp].r - tree[tmp].l + 1)
                                    tr = tree[tmp].r + 1;
                                else
                                    tr = tree[tmp<<1].r + 1;
                            }
                        }
                        //printf("tl:%d  tr:%d\n",tl,tr);
                        ans = tr - tl - 1;
                        printf("%d\n",ans);
                    }
                }
            }
            //print(1,1,n);
            //putchar(10);
        }
    }
    return 0;
}
//hdu1031MS	2032K
//poj2068K	391MS



你可能感兴趣的:(线段树)