poj2892&&HDU1540 Tunnel Warfare(线段树)

poj都给hint,D x摧毁x,R恢复最近摧毁的点,Q x查看和x相连的点的个数(未摧毁)。

做法:

1)训练计划上把这题分到静态二叉检索树上,所以先想到用线段树做,线段树维护每段中被消除点的最大位置和最小位置,摧毁点k就把k添加到位置k,查询k,就查询段[1,k]的中的最大值和[k,n]的最小值,然后相减得到结果。初始化时最大值为0,最小值为n+1,这样在区间如果没有被摧毁的点也能返回正确结果,有也能正确更新。查询和插入都是logn

2)第二种做法是把摧毁的点的位置都放到有序序列里,查询k时二分查找比k大的第一个数,和比k小的第一个数,如果k没被摧毁,相减就是结果。而这个有序序列用set就行了,插入和查询也都是logn,直接用set的里lower_bound,用algorithm里的lower_bound会超时(对于set,list这种不能随机访问的容器,algorithm

中的lower_bound可能变成了nlogn)。

线段树做法

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<algorithm>
#include<string>
#include<queue>
#include<stack>
#include<set>
#include<map>
#define maxn 55000
#define LL long long

using namespace std;

#define ls o*2,l,m
#define rs o*2+1,m+1,r
int MAX;
struct node
{
	int l,r;
	int mmax,mmin;
	void set(int l,int r){ this->l=l,this->r=r,this->mmax=0,this->mmin=MAX;}
	int getM(){ return (l+r)/2; }
}seg[maxn*3];

void build(int o,int l,int r)
{
	seg[o].set(l,r);
	int m=(l+r)/2;
	if(l<r)
	{
		build(ls);
		build(rs);
	}
}

void update(int o,int pos,int v)
{
	if(seg[o].l==seg[o].r)
	{
		if(v!=-1)
		{
			seg[o].mmax=v;
			seg[o].mmin=v;
		}
		else
		{
			seg[o].mmax=0;
			seg[o].mmin=MAX;
		}
		return;
	}
	int m=seg[o].getM();
	if(pos<=m)
		update(o*2,pos,v);
	else
		update(o*2+1,pos,v);
	seg[o].mmax=max(seg[o*2].mmax,seg[o*2+1].mmax);
	seg[o].mmin=min(seg[o*2].mmin,seg[o*2+1].mmin);
}

int queryMAX(int o,int ql,int qr)
{
	if(ql>qr)
		return -1;
	if(ql<=seg[o].l && seg[o].r<=qr)
		return seg[o].mmax;
	int m=seg[o].getM();
	int mmax=-1;
	if(ql<=m)
		mmax=max(mmax,queryMAX(o*2,ql,qr));
	if(qr>m)
		mmax=max(mmax,queryMAX(o*2+1,ql,qr));
	return mmax;
}

int queryMIN(int o,int ql,int qr)
{
	if(ql>qr)
		return -1;
	if(ql<=seg[o].l && seg[o].r<=qr)
		return seg[o].mmin;
	int m=seg[o].getM();
	int mmin=INT_MAX;
	if(ql<=m)
		mmin=min(mmin,queryMIN(o*2,ql,qr));
	if(qr>m)
		mmin=min(mmin,queryMIN(o*2+1,ql,qr));
	return mmin;
}

int main()
{
	int n, m;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		MAX=n+1;
		build(1,0,n+1);
		char op[10];
		int pos;
		stack<int>sta;
		for(int i=0;i<m;++i)
		{
			scanf("%s",op);
			if(op[0]=='D')
			{
				scanf("%d",&pos);
				update(1,pos,pos);
				sta.push(pos);
			}
			else if(op[0]=='R')
			{
				pos=sta.top();
				sta.pop();
				update(1,pos,-1);
			}
			else
			{
				scanf("%d",&pos);
				int L=queryMAX(1,1,pos);
				int R=queryMIN(1,pos,n);
				printf("%d\n",max(0,R-L-1));
			}
		}
	}
}

set二分

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<algorithm>
#include<string>
#include<queue>
#include<stack>
#include<set>
#include<map>
#define maxn 55000
#define LL long long

using namespace std;

set<int>_set;
int main()
{
	int n,m;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		_set.clear();
		stack<int>sta;
		char op[10];int pos;
		_set.insert(0),_set.insert(n+1);
		for(int i=0;i<m;++i)
		{
			scanf("%s",op);
			if(op[0]=='D')
			{
				scanf("%d",&pos);
				_set.insert(pos);
				sta.push(pos);
			}
			else if(op[0]=='R')
			{
				pos=sta.top();
				sta.pop();
				_set.erase(pos);
			}
			else
			{
				scanf("%d",&pos);
	//			set<int>::iterator iter=lower_bound(_set.begin(),_set.end(),pos);//超时,看来set里的lower_bound是优化过的,比较快
				set<int>::iterator iter=_set.lower_bound(pos);
				int R=*iter;
				int L=*(--iter);
				if(R==pos)
					printf("0\n");
				else
					printf("%d\n",R-L-1);
			}
		}
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值