zoukankan      html  css  js  c++  java
  • HDU 1540 / POJ 2892 Tunnel Warfare (单点更新,区间合并,求包含某点的最大连续个数)

    题意:一条线上有n个点,D x是破坏这个点,Q x是表示查询x所在的最长的连续的点的个数,R是恢复上一次破坏的点。

    思路:这题的关键是查询。
      将被毁的村庄看成空位,当查询某个点的时候,如果我们知道它左边最近的空位a和右边最近的空位b,
      那么我们只要查询区间[a,b]中的个数,即为答案,因为[a,b]之间不可能有空位存在了。
      那么如何获取这样的a和b呢,这个就和HDU 4302 Holedox Eating 差不多了。
      对每个节点,存储该区间中 空位的最大位置 和 空位的最小位置,还有 该区间村庄的个数。
      这样当我们查询某点x的时候,那么我们只要查询[1,x]区间最大的空位编号a,[x,n]区间最小的空位编号为b,
      那么a~b之间就没别的空位,查询[a,b]区间的村庄个数即为答案。

    HDU 1540 比POJ 2892 要坑,如果在HDU 1540 要注意一下几点。
    如下:
    (1)多case
    (2)某个村庄可以被毁坏多次(必须全部入栈),但只需要一次就能将其恢复(见下面的这组数据)
    (3)D 3 D 2 D 1 D 1 D 2
    R 恢复2
    R 恢复1
    R 恢复3

    #include <iostream>
    #include <stdio.h>
    #include <set>
    #include <string.h>
    #define lson rt<<1,L,mid
    #define rson rt<<1|1,mid+1,R
    
    using namespace std;
    const int maxn=50005;
    const int INF=0X3f3f3f3f;
    int n,m;
    int vis[maxn];   //vis[i]=1表示该村庄被毁
    int last[maxn];  //栈存取被毁村庄的编号
    int idx=-1;
    struct Node {
        int sum;     //该区间中村庄的个数
        int maxval;  //该区间中被毁村庄的最大编号
        int minval;  //该区间中被毁村庄的最小编号
    } tree[maxn<<2];
    
    void build(int rt,int L,int R) {
        tree[rt].sum=R-L+1;
        tree[rt].maxval=-INF;
        tree[rt].minval=INF;
        if(L==R)
            return;
        int mid=(L+R)>>1;
        build(lson);
        build(rson);
    }
    void pushUp(int rt) {
        tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum;
        tree[rt].maxval=max(tree[rt<<1].maxval,tree[rt<<1|1].maxval);
        tree[rt].minval=min(tree[rt<<1].minval,tree[rt<<1|1].minval);
    }
    
    void update(int rt,int L,int R,int x,int c) {
        if(L==R) {
            tree[rt].sum=c;
            //c=1表示恢复村庄,c=0表示摧毁该村庄
            if(c) {
                tree[rt].minval=INF;
                tree[rt].maxval=-INF;
            } else {
                tree[rt].maxval=L;
                tree[rt].minval=L;
            }
            return;
        }
        int mid=(L+R)>>1;
        if(x<=mid)
            update(lson,x,c);
        else
            update(rson,x,c);
        pushUp(rt);
    }
    //查询左区间被毁村庄的最大编号
    int queryMax(int rt,int L,int R,int l,int r) {
        if(l<=L&&R<=r) {
            return tree[rt].maxval;
        }
        int ret;
        int mid=(L+R)>>1;
        if(r<=mid)
            ret=queryMax(lson,l,r);
        else if(l>mid)
            ret=queryMax(rson,l,r);
        else {
            ret=max(queryMax(lson,l,mid),queryMax(rson,mid+1,r));
        }
        return ret;
    }
    //查询右区间被毁村庄的最小编号
    int queryMin(int rt,int L,int R,int l,int r) {
        if(l<=L&&R<=r) {
            return tree[rt].minval;
        }
        int ret;
        int mid=(L+R)>>1;
        if(r<=mid)
            ret=queryMin(lson,l,r);
        else if(l>mid)
            ret=queryMin(rson,l,r);
        else {
            ret=min(queryMin(lson,l,mid),queryMin(rson,mid+1,r));
        }
        return ret;
    }
    //查询该区间的村庄个数
    int querySum(int rt,int L,int R,int l,int r) {
        int ret=0;
        if(l<=L&&R<=r) {
            return tree[rt].sum;
        }
        int mid=(L+R)>>1;
        if(l<=mid)
            ret+=querySum(lson,l,r);
        if(r>mid)
            ret+=querySum(rson,l,r);
        return ret;
    }
    int main() {
        memset(vis,0,sizeof(vis));
        char str[5];
        int x,y,a;
        while(scanf("%d%d",&n,&m)!=EOF) {
            memset(vis,0,sizeof(vis));
            idx=-1;
            build(1,1,n);
            for(int i=1; i<=m; i++) {
                scanf("%s",str);
                if(str[0]=='D') {
                    scanf("%d",&a);
                    update(1,1,n,a,0);
                    last[++idx]=a;   //不管之前是否被毁,都加入到栈
                    vis[a]=1;
                } else if(str[0]=='R') {
                    /*
                      可能有的已经之前被修复了,所以这里要用while先判断一下
                    */
                    while(vis[last[idx]]==0 &&idx>=0)
                        idx--;
                    if(idx==-1)
                        continue;   //如果没有被毁的,则直接忽视就行
                    update(1,1,n,last[idx],1);
                    vis[last[idx]]=0;
                    idx--;
                } else {
                    scanf("%d",&a);
                    if(vis[a]==1)
                        printf("0
    ");
                    else {
                        //x为a左边距离a最近的空位(即被拆的村庄)
                        //y为a右边距离a最近的空位(即被拆的村庄)
                        x=queryMax(1,1,n,1,a);
                        y=queryMin(1,1,n,a,n);
    //printf("%d,%d
    ",x,y);
                        printf("%d
    ",querySum(1,1,n,x,y));  //因为x、y之间没有被毁的村庄,所以查询x、y之间的村庄个数即可
                    }
                }
            }
        }
        return 0;
    }

    还有另一个做法,那就是每个节点记录的是:左端开始连续的个数,右端开始连续的个数,最大的连续个数。
    当查询某点x的时候,查询[1,x-1]区间,合并成t1节点;查询[x+1,n],合并成t2节点,答案即为t1.rsum+1+t2.lsum。

    #include <iostream>
    #include <stdio.h>
    #include <set>
    #include <string.h>
    #define lson rt<<1,L,mid
    #define rson rt<<1|1,mid+1,R
    
    using namespace std;
    const int maxn=50005;
    const int INF=0X3f3f3f3f;
    int n,m;
    int vis[maxn];   //vis[i]=1表示该村庄被毁,=0表示村庄没被毁
    int last[maxn];  //栈存取被毁村庄的编号
    int idx=-1;
    struct Node {
        int lsum,msum,rsum;  //左连续的最大值,连续的最大值,右连续的最大值
        int len;
    } tree[maxn<<2];
    
    void build(int rt,int L,int R) {
        tree[rt].len=tree[rt].lsum=tree[rt].rsum=tree[rt].msum=R-L+1;
        if(L==R)
            return;
        int mid=(L+R)>>1;
        build(lson);
        build(rson);
    }
    
    void pushUp(Node &rt,Node &ls,Node &rs){
        rt.lsum=ls.lsum;
        rt.rsum=rs.rsum;
        if(ls.lsum==ls.len)
            rt.lsum+=rs.lsum;
        if(rs.rsum==rs.len)
            rt.rsum+=ls.rsum;
        rt.msum=ls.rsum+rs.lsum;
        rt.msum=max(rt.msum,max(ls.msum,rs.msum));
    }
    void update(int rt,int L,int R,int x,int c) {
        if(L==R) {
            //c=1表示恢复村庄,c=0表示摧毁该村庄
            if(c) {
                tree[rt].lsum=tree[rt].rsum=tree[rt].msum=1;
            } else {
                tree[rt].lsum=tree[rt].rsum=tree[rt].msum=0;
            }
            return;
        }
        int mid=(L+R)>>1;
        if(x<=mid)
            update(lson,x,c);
        else
            update(rson,x,c);
        pushUp(tree[rt],tree[rt<<1],tree[rt<<1|1]);
    }
    //将对应区间[l,r]合并成一个节点返回
    Node query(int rt,int L,int R,int l,int r){
        if(l<=L&&R<=r){
            return tree[rt];
        }
        int mid=(L+R)>>1;
        int length=(R-L+1);
        Node tmp,r1,r2;
        if(r<=mid)
            tmp=query(lson,l,r);
        else if(l>mid)
            tmp=query(rson,l,r);
        else{
            r1=query(lson,l,mid);
            r2=query(rson,mid+1,r);
            tmp.len=r1.len+r2.len;
            pushUp(tmp,r1,r2);
        }
        return tmp;
    }
    
    int main() {
        memset(vis,0,sizeof(vis));
        Node t1,t2;
        char str[5];
        int a,ans;
        while(scanf("%d%d",&n,&m)!=EOF) {
            memset(vis,0,sizeof(vis));
            idx=-1;
            build(1,1,n);
            for(int i=1; i<=m; i++) {
                scanf("%s",str);
                if(str[0]=='D') {
                    scanf("%d",&a);
                    update(1,1,n,a,0);
                    last[++idx]=a;   //不管之前是否被毁,都加入到栈
                    vis[a]=1;
                } else if(str[0]=='R') {
                    /*
                      可能有的已经之前被修复了,所以这里要用while先判断一下
                    */
                    while(vis[last[idx]]==0 &&idx>=0)
                        idx--;
                    if(idx==-1)
                        continue;   //如果没有被毁的,则直接忽视就行
                    update(1,1,n,last[idx],1);
                    vis[last[idx]]=0;
                    idx--;
                } else {
                    scanf("%d",&a);
                    if(vis[a]==1)
                        printf("0
    ");
                    else {
                        //为方便起见,1和n单独分出来
                        if(a==1){
                            t1=query(1,1,n,a+1,n);
                            ans=t1.lsum+1;
                        }
                        else if(a==n){
                            t1=query(1,1,n,1,a-1);
                            ans=t1.rsum+1;
                        }
                        else{
                            t1=query(1,1,n,1,a-1);
                            t2=query(1,1,n,a+1,n);
                            ans=t1.rsum+1+t2.lsum;
                        }
                        printf("%d
    ",ans);
                    }
                }
            }
        }
        return 0;
    }
  • 相关阅读:
    【微服务架构】SpringCloud之Ribbon
    SpringCloud之Eureka(注册中心集群篇)
    Eureka简介
    两行代码 搞定计数
    HBase详细概述
    电商项目介绍---说的很好
    面试:----Nginx的一理解
    redis介绍
    Linux操作系统介绍
    什么是Solr
  • 原文地址:https://www.cnblogs.com/chenxiwenruo/p/3418309.html
Copyright © 2011-2022 走看看