动态最值 [ 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;
}