【2018/10/12】校内模拟赛

本文详细解析了线段树在区间修改和查询问题中的应用,通过具体题目讲解了如何利用线段树进行区间最小值的修改和查询,特别关注了在数据范围内优化查询效率的方法。

题一

题二

题三

 

分析(题一) 

一道模拟题

但模拟也是有很多坑的……

写出一道题后,一定要自己试很多很多很多组不同的数据

比如这道题,几乎我试的每一组数据都找出了我程序的bug,好险~

 

分析(题二)

好题!!!!

应该是和松鼠聚会这道题本质一样的

首先我们需要推导出任意两个点(x,y) (a,b)之间能够到达彼此所需要走的最短步数,由于可以斜着走,那么我们肯定会优先走对角线,然后再直线走到目标位置,对角线的步数:min(|x-a|,|y-b|),直线的话再推一下

就可以得到答案max(|x-a|,|y-b|),这不就是切比雪夫距离(⊙_⊙)?

自然想到将其转为曼哈顿距离

常用套路一用,O(∩_∩)O哈哈~(妙哉,虽然我没想出来)

 

分析(题三)

我的暴力……居然爆0

究其原,还是思维有漏洞,大大的bug!!!

想贴一下自己的代码,然后好好吐槽一下自己

改了下面两个地方后,就多了33分,感谢wcr大佬!!!Orz

再次感谢cyk大佬!!!让我又一跃成了50分

#include<bits/stdc++.h>
#define in read()
#define N 500009
#define lc (k<<1)
#define rc (k<<1)|1
#define inf 1000000009
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<3)+(res<<1)+ch-'0';
		ch=getchar();
	}
	return f==1?res:-res;
}
int n,a[N],minn[4*N],m,lzy[4*N],b[N];
void build(int k,int l,int r){
	lzy[k]=inf;
	if(l==r){
		minn[k]=a[l];
		return;
	}
	int mid=l+r>>1;
	build(lc,l,mid);build(rc,mid+1,r);
	minn[k]=min(minn[lc],minn[rc]);
}
void pushdown(int k){
	if(lzy[k]!=inf){
		if(minn[lc]<lzy[k]) {minn[lc]=lzy[k];lzy[lc]=lzy[k];}
		if(minn[rc]<lzy[k]){minn[rc]=lzy[k];lzy[rc]=lzy[k];}
		lzy[k]=inf;
	}
}
void modify(int k,int l,int r,int x,int y,int z){
	if(x<=l&&r<=y){
		//lzy[k]=z;minn[k]=z;//你看pushdown我都知道要取max……,但这个地方居然没有取???
        lzy[k]=max(lzy[k],z);minn[k]=max(minn[k],z);
		return;
	}
	pushdown(k);
	int mid=l+r>>1;
	if(x<=mid) modify(lc,l,mid,x,y,z);
	if(y>mid) modify(rc,mid+1,r,x,y,z);
	minn[k]=min(minn[lc],minn[rc]);
}
int query(int k,int l,int r,int x,int y){
	if(x<=l&&r<=y){
		return minn[k];
	}
	pushdown(k);
	int mid=l+r>>1,res=inf;
	if(x<=mid) res=min(res,query(lc,l,mid,x,y));
	if(y>mid) res=min(res,query(rc,mid+1,r,x,y));
	return res;
} 
int main(){
	n=in;
	int i,j,k;
	for(i=1;i<=n;++i) a[i]=in;
	if(n<=3000){
		m=in;int op,l,r,x;
		while(m--){
			op=in;l=in;r=in;x=in;
			if(op==1){
				for(int i=l;i<=r;++i)
					if(a[i]<x) a[i]=x;
			}
			else{
				k=in;
				memcpy(b,a,sizeof(a));
				sort(b+l,b+r+1);
				int pos=lower_bound(b+l,b+r+1,x)-b;
				pos--;while(b[pos]==x) pos--;
				if(pos>=k+l-1){//////////相对位置的关系
					 for(int i=l;i<=l+k-1;++i) printf("%d ",b[i]);////////怎么可以从1开始!!!!
					 printf("\n");
				}
				else printf("-1\n");
			}
		}
	}
	else{
		build(1,1,n);
		m=in;
		int op,l,r,x;
		while(m--){
			op=in;l=in;r=in;x=in;
			if(op==1){
				modify(1,1,n,l,r,x);
			}
			else{
				k=in;
				if(k==1){
					int mn=query(1,1,n,l,r);
					if(mn>=x) printf("-1\n");////////如果只判等的话,那要是这个区间的最小值大于x呢?还不是不行
					else printf("%d\n",mn);
				}
			}
		}
	}
	return 0;
}

 

现在来讲一下正解吧 

修改比较简单,就是线段树的区间修改

因为现在我们将区间[l,r]中所有小于 x 的数都赋为  x ,那么 x 肯定就是此时的区间最小值

维护一下就好了

只是需要注意,若这个区间的最小值本就大于  x  ,那我们是修改不了的

所以取个max

然后对于查询

我们观察一下数据范围,可以发现 k 总和都不超过5*10^6,是个突破口

所以我们查询区间[L,R]中比 X小的最小的 K 个数,就可以变成求 K 次区间最小值,每求完一次就暂时将这个位置的值赋为INF,避免其对下次求最小值有影响,然后在这次操作完了之后将所有的“最小值”还原其本来的值

操作细节:由于我们需要还原,所以我们得记录下每个最小值的位置。那就用一个二元组(pair<int,int>)来存储线段树中每个节点的信息。first  -- > 值,second-->位置

 

代码走一波(稍微需要卡一下常)

#include<bits/stdc++.h>
#define in read()
#define N 500009
#define lc (k<<1)
#define rc (k<<1)|1
#define inf 1000000009
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<3)+(res<<1)+ch-'0';
		ch=getchar();
	}
	return f==1?res:-res;
}
int n,a[N],m,lzy[4*N];
pair<int,int>  minn[4*N];
inline void build(int k,int l,int r){
	lzy[k]=0;
	if(l==r){
		minn[k]=make_pair(a[l],l);
		return;
	}
	int mid=l+r>>1;
	build(lc,l,mid);build(rc,mid+1,r);
	minn[k]=min(minn[lc],minn[rc]);
}
inline void pushdown(int k){
	
	if(minn[lc].first<lzy[k]) {minn[lc].first=lzy[k];lzy[lc]=lzy[k];}
	if(minn[rc].first<lzy[k]){minn[rc].first=lzy[k];lzy[rc]=lzy[k];}
	lzy[k]=0;
	
}
inline void modify(int k,int l,int r,int x,int y,int z){
	if(x<=l&&r<=y){
		lzy[k]=max(z,lzy[k]);
		minn[k].first=max(z,minn[k].first);
		return;
	}
	if(lzy[k]) pushdown(k);
	int mid=l+r>>1;
	if(x<=mid) modify(lc,l,mid,x,y,z);
	if(y>mid) modify(rc,mid+1,r,x,y,z);
	minn[k]=min(minn[lc],minn[rc]);
}
pair<int,int> query(int k,int l,int r,int x,int y){
	if(x<=l&&r<=y)	return minn[k];
	if(lzy[k]) pushdown(k);
	int mid=l+r>>1;
	pair<int,int> res;res.first=inf;
	if(x<=mid) res=min(res,query(lc,l,mid,x,y));
	if(y>mid) res=min(res,query(rc,mid+1,r,x,y));
	return res;
} 
inline void insert(int k,int l,int r,int p,int x){
	if(l==r){
		minn[k].first=x;
		return ;
	}
	if(lzy[k]) pushdown(k);
	int mid=l+r>>1;
	if(p<=mid) insert(lc,l,mid,p,x);
	else insert(rc,mid+1,r,p,x);
	minn[k]=min(minn[lc],minn[rc]);
}
int main(){
	n=in;
	int i,j,k;
	for(i=1;i<=n;++i) a[i]=in;
	build(1,1,n);
	m=in;
	int op,l,r,x;
	while(m--){
		op=in;l=in;r=in;x=in;
		if(op==1){
			modify(1,1,n,l,r,x);
		}
		else{
			k=in;int flag=0;
			vector<pair<int,int> > V;
			for(i=1;i<=k;++i){
				pair<int,int> mn=query(1,1,n,l,r);
				if(mn.first>=x) {flag=1;break;} 
				V.push_back(mn);
				insert(1,1,n,mn.second,inf);
			}
			if(!flag) for(i=0;i<V.size();++i) printf("%d ",V[i].first);
			else printf("-1");
			printf("\n");
			for(i=0;i<V.size();++i) insert(1,1,n,V[i].second,V[i].first);
		}
	}	
	return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值