hdu 1540 Tunnel Warfare (set)

小记:平衡树或者线段树都可以


思路:

1,、平衡树解法:

因为是从小到大排序好的,所以可以用到平衡树来解。

每碰到一个要销毁的,就插入到平衡树,最开始插入一个0,和n+1,两个值,为了求解用的,

这样我们可以知道,在平衡树里,是一个销毁的顺序表,例如 0 1 3 5 9 12  n=11

那么当我们要查6时,我们用lowerbound()方法可以求得在set里第一个大于等于6的位置是哪个,很明显是9

那么我们再将其往回退一个,也就是求得最后一个小于等于6的位置,那个位置的值是5,那么6的连续区间存活的村庄个数就是9-5-1 = 3 (左右两端都不包括,所以-1)

细心的读者会发现,等于的情况就是我们要查的村庄被摧毁了。上面说的回退其实并不正确,因为当返回的就是等于的位置时,那么我们就不必回退了,这个一个if判断一下就OK


2、线段树解法:

定义一个结构体:

包含区间[l,r]

当前区间左端点往右最长的连续段长度ll, 以及右端点的rl, 和当前区间最长的连续段长度ml


关键在更新查询两个操作, 注意边界问题

[a, b] - 节点i

[a, c] - 节点i<<1   [c+1, b] - 节点(i<<1)|1

最大连续段的更新:a[i].ml = max(a[i<<1].ml, a[(i<<1)|1].ml), a[i].ml = max(a[i].ml, a[i<<1].rl + a[(i<<1)|1].ll);

左连续段的更新:a[i].ll=a[i<<1].ll;if(a[i<<1].ll==a[i<<1].r-a[i<<1].l+1)a[i].ll+=a[(i<<1)|1].ll;

右连续段的更新:a[i].rl=a[(i<<1)|1].rl;if(a[(i<<1)|1].rl==a[(i<<1)|1].r-a[(i<<1)|1].l+1)a[i].rl+=a[i<<1].rl;


查询:

如果再当前区间的左儿子的右边连续段内:query(i<<1,t)+query((i<<1)|1,mid+1); 否则:query(i<<1,t);

相反在当前区间的右儿子的左连续段内:query((i<<1)|1,t)+query(i<<1,mid);否则:query((i<<1)|1,t);


1、平衡树代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <map>
#include <set>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>

using namespace std;

#define mst(a,b) memset(a,b,sizeof(a))
#define eps 10e-8

const int MAX_ = 10010;
const int N = 100010;
const int INF = 0x7fffffff;


int main(){
	int n, m, x;
	char c;
	set<int>st;
	set<int>::iterator it;
	while(~scanf("%d%d", &n, &m)){
	    stack<int>s;
	    st.clear();
	    st.insert(0);
	    st.insert(n+1);
        for(int i = 0; i < m; ++i){
            getchar();
            scanf("%c",&c);
            if(c != 'R')scanf("%d", &x);
            else {
                int x = s.top();s.pop();
                it = st.lower_bound(x);
                st.erase(it);continue;
            }
            if(c == 'D'){
                s.push(x);
                st.insert(x);
            }
            else {
                it = st.lower_bound(x);
                if(*it > x)--it;

                if(*it == x){
                    puts("0");
                }
                else {
                    int l,r;
                    l = *it++;r = *it;
                    printf("%d\n",r-l-1);
                }
            }
        }
	}
	return 0;
}


2、线段树代码:

#include<iostream>
#include <stdio.h>

using namespace std;
const int maxn = 50010;

int Q[maxn];
int stack[maxn];

bool isok[maxn];

//seg tree
struct node {
    int l,r;
    int ml; //最大长度,方便下面计算而已//以上3个值均不变
    int ll,rl;  //分别表示从左节点开始的长度,从右节点开始的长度
};

node tree[maxn*3];

void maketree(int a,int b,int k) {
    tree[k].l = a;
    tree[k].r = b;
    tree[k].ml = b-a+1;

    tree[k].ll = b-a+1;
    tree[k].rl = b-a+1;

    if(a == b)
        return;

    int mid = (a+b)>>1;

    maketree(a,mid,k<<1);
    maketree(mid+1,b,(k<<1)|1);
}

void update(int p,int i,int val) {
    if(tree[i].l == tree[i].r) {
        if(!val) {
            tree[i].ll = 0;
            tree[i].rl = 0;
            tree[i].ml = 0;
            isok[p] = 0; //del
        } else {
            tree[i].ll = 1;
            tree[i].rl = 1;
            tree[i].ml = 1;
            isok[p] = 1;//rebuild
        }
        return;
    }
    int mid = (tree[i].l + tree[i].r)>>1;
    if(p <= mid)
        update(p,i<<1,val);
    else
        update(p,(i<<1)|1,val);

    tree[i].ll=tree[i<<1].ll;
    tree[i].rl=tree[(i<<1)|1].rl;
    tree[i].ml=max(tree[i<<1].ml,tree[(i<<1)|1].ml);
    tree[i].ml=max(tree[i].ml,tree[i<<1].rl+tree[(i<<1)|1].ll);

    if(tree[i<<1].ll==tree[i<<1].r-tree[i<<1].l+1)
        tree[i].ll+=tree[(i<<1)|1].ll;

    if(tree[(i<<1)|1].rl==tree[(i<<1)|1].r-tree[(i<<1)|1].l+1)
        tree[i].rl+=tree[i<<1].rl;

}

int query(int i,int t) {
    if(tree[i].l == tree[i].r || tree[i].ml==0 ||tree[i].ml==tree[i].r-tree[i].l+1) {
        return tree[i].ml;
    }
    int mid=(tree[i].l+tree[i].r)>>1;
    if(t<=mid) {
        if(t >= tree[i<<1].r - tree[i<<1].rl+1)
            return query(i<<1,t)+query((i<<1)|1,mid+1);
        else return query(i<<1,t);
    } else {
        if(t<=tree[(i<<1)|1].l+tree[(i<<1)|1].ll-1)
            return query((i<<1)|1,t)+query(i<<1,mid);
        else return query((i<<1)|1,t);
    }
}


int main() {
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF) {
        //init
        for(int i=1; i<=n; i++)
            isok[i] = 1;

        maketree(1,n,1);

        char s[2];
        int top = 0;
        int pos;
        for(int i=1; i<=m; i++) {
            scanf("%s",s);
            if(s[0] == 'D') {
                scanf("%d",&pos);
                stack[++top] = pos;

                if(isok[pos])   //当结点还完好,才要破坏它
                    update(pos,1,0);
            } else if(s[0] == 'Q') {
                scanf("%d",&pos);
                if(isok[pos] == 0) //如果pos位置已损坏..直接输出0
                    printf("0\n");
                else
                    printf("%d\n",query(1,pos));
            } else {
                if( top == 0)
                    continue;

                update(stack[top--],1,1);
            }
        }
    }
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值