现在的位置: 首页 > 综合 > 正文

【线段树】动态最值 SCOI2006 minmax

2014年01月20日 ⁄ 综合 ⁄ 共 2472字 ⁄ 字号 评论关闭

动态最值 [ SCOI2006 ]

minmax

5s/256MB

有一个包含n个元素的数组,要求实现以下操作:

DELETE k:删除位置k上的数。右边的数往左移一个位置。

QUERY i j:查询位置i~j上所有数的最小值和最大值。

例如有10个元素:

位置

1

2

3

4

5

6

7

8

9

10

元素

1

5

2

6

7

4

9

3

1

5

QUERY 2 8的结果为2 9。依次执行DELETE 3和DELETE 6(注意这时删除的是原始数组的元素7)后数组变为:

位置

1

2

3

4

5

6

7

8

元素

1

5

6

7

4

3

1

5

QUERY 2 8的结果为1 7。

 

【输入】

输入文件minmax.in第一行包含两个数n, m,表示原始数组的元素个数和操作的个数。第二行包括n个数,表示原始数组。以下m行,每行格式为1 k或者2 i j,其中第一个数为1表示删除操作,为2表示询问操作。

 

【输出】

输出文件minmax.out对每个询问操作输出一行,包括两个数,表示该范围内的最小值和最大值。

 

【样例输入】

10 4

1 5 2 6 7 4 9 3 15

2 2 8

1 3

1 6

2 2 8

 

【样例输出】

2 9

1 7

 

【限制】

50%的数据满足1<=n, m<=104,删除操作不超过100个

100%的数据满足1<=n, m<=106, 1<=m<=106

对于所有的数据,数组中的元素绝对值均不超过109






这一题和前面operation那一题都很难

但不同的是,operation那一题思维难度不大,而这一题要稍微想想


题目已经很明确地说了是动态的区间,那么我们写线段树又怎么写成动态的呢?


其实根本不用写动态线段树,我们只需要多维护一个节点个数即可


我们先想想线段树在往下传的时候是m=(l+r)/2,然后下面再从 [ l , m ] 和 [ m + 1 , r ] 这两个区间内找


那么我们可以维护一个 sum[ ] 来表示当前区间还剩多少个点,


比如我们要找区间 [ a , b ],那么我们只需把当前节点 p 的左儿子p*2 的sum值和 a 来作比较,如果 a <= sum[p*2] 的话就说明 [ a , b ] 这个区间在左儿子中有一部分

这样就可以解决上面所说的动态的问题了


cena测试结果



/*

http://blog.csdn.net/jiangzh7

By Jiangzh
*/
#include<cstdio>
const int N=1000000+10;
const int inf=0x3f3f3f3f;

int n,m;
int _min[N*4],_max[N*4],sum[N*4];

inline int Min(int a,int b){return a<b?a:b;}
inline int Max(int a,int b){return a>b?a:b;}
struct status
{
	int min,max;
	status(){min=inf;max=-inf;}
};

void renew(int p)
{
	sum[p]=sum[p<<1]+sum[(p<<1)+1];
	_min[p]=Min(_min[p<<1],_min[(p<<1)+1]);
	_max[p]=Max(_max[p<<1],_max[(p<<1)+1]);
}

void in_tree(int p,int l,int r,int a,int c)
{
	if(l==r && l==a)
	{
		_min[p]=_max[p]=c;
		sum[p]=1;
		return;
	}
	int m=(l+r)>>1;
	if(a<=m) in_tree(p<<1,l,m,a,c);
	if(a>m) in_tree((p<<1)+1,m+1,r,a,c);
	renew(p);
}

void segment_delete(int p,int l,int r,int a)
{
	if(l==r)
	{
		_min[p]=inf;
		_max[p]=-inf;
		sum[p]=0;
		return;
	}
	int m=(l+r)>>1;
	if(a<=sum[p<<1]) segment_delete(p<<1,l,m,a);
	else segment_delete((p<<1)+1,m+1,r,a-sum[p<<1]);
	renew(p);
}

status query(int p,int l,int r,int a,int b)
{
	status temp;
	if(b-a+1==sum[p])
	{
		temp.min=_min[p];
		temp.max=_max[p];
		return temp;
	}
	int m=(l+r)>>1;
	if(b<=sum[p<<1]) temp=query(p<<1,l,m,a,b);
	else if(a>sum[p<<1]) temp=query((p<<1)+1,m+1,r,a-sum[p<<1],b-sum[p<<1]);
	else{
		status x1,x2;
		x1=query(p<<1,l,m,a,sum[p<<1]);
		x2=query((p<<1)+1,m+1,r,1,b-sum[p<<1]);
		temp.min=Min(x1.min,x2.min);
		temp.max=Max(x1.max,x2.max);
	}
	return temp;
}

void work()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		in_tree(1,1,n,i,x);
	}
	for(int i=1;i<=m;i++)
	{
		int op,x,y;
		scanf("%d",&op);
		if(op==1)//delete
		{
			scanf("%d",&x);
			segment_delete(1,1,n,x);
		}
		else{//query
			scanf("%d%d",&x,&y);
			status ans=query(1,1,n,x,y);
			printf("%d %d\n",ans.min,ans.max);
		}
		//for(int p=1;p<=15;p++) printf("i:min=%d max=%d sum=%d\n",_min[p],_max[p],sum[p]);puts("");
	}
}

int main()
{
	freopen("minmax.in","r",stdin);
	freopen("minmax.out","w",stdout);
	work();
	return 0;
}

抱歉!评论已关闭.