NOIP模拟 数据结构(线段树)

【题目描述】

在看了 jiry_2 的课件《Segment Tree Beats!》后,小 O 深深沉迷于这种能单次 O(logn) 支持区间与一个数取 min/max,查询区间和等信息的数据结构,于是他决定做一道与区间与 一个数取 min/max 的好题。

这题是这样的:你得到了一个长度为 n 的数列{ai},要求支持以下 2 种操作:第一种是给 定 L,R,X,要求把区间中比 X 小的数字全部修改为 X;第二种是给定 L,R,K,X,查询区间中比 X 小的最小的 K 个数,并且将它们升序输出,没有则输出-1。

小 O 觉得这题太简单了,于是把这题丢给了你,请你帮忙实现。

下发文件中有 jiry_2 的课件《Segment Tree Beats!》,不保证其与解题有关。(其实真的没关)

【输入格式】

第一行一个数字 n 表示数列长度,

第二行 n 个数字分别表示 a1....an,

第三行一个数字 m 表示操作次数,

接下来 m 行每行表示一次操作,

第一个数 op 表示操作类型,op 可能是 1 或 2,

如果 op=1,后面有 L,R,X 三个正整数表示把区间[L,R]中比 X 小的数字全部改成 X

如果 op=2,后面有 L,R,X,K 四个正整数表示查询区间[L,R]中比 X 小的最小的 K 个数

【输出格式】

对于每个 op=2,输出一行,

如果比 X 小的数达到了 K 个,升序输出最小的 K 个数,

如果比 X 小的数小于 K 个,输出一行一个-1 即可.

【样例输入】

3

1 2 3

4

1 1 2 2

2 1 3 1 3

2 1 3 2 1

2 1 3 3 2

【样例输出】

-1

-1

2 2

【备注】

本题共 6 个测试点,不采用 subtask 评测,但每个测试点分值不同。

对于全部数据,满足 1<=n,m<=500000,1<=L<=R<=n,1<=K<=n,1<=Ai,X<=10^9,对于所有 操作 2 中的 K,K 的总和不超过 5e6。

~1:12pts,满足 1<=n,m<=3000;

~2:7pts,满足 1<=n,m<=100000,没有操作 1,且对于所有操作 2 有 K=1;

~3:23pts,满足 1<=n,m<=100000,对于所有操作 2 有 K=1;

~4:37pts,满足 1<=n,m<=100000,没有操作 1;

~5:6pts,满足 1<=n,m<=100000;

~6:15pts,无特殊限制。

【题目分析】

这道题可以跑4s。。。。最后总共跑了9999ms简直暗示着什么。。。。。。

好了不扯了,反正又是道不可做题(恭喜WCR大佬暴力50+RANK1),下来看了看题解还算是基本能搞懂,最后各种TLE,然后发现加个inline就过了。。。。花式难受。。。。。

讲讲正解:维护一颗线段树,让他能记录区间最小值以及区间最小值的位置。所以整个线段树用pair类型存储,第一位储存区间最小值,第二位存储区间最小值的位置。对于所有操作1,我们发现就是在给定区间里让所有数与x去max,如果当前区间最小值已经大于x,那么显然这个操作不会产生任何效果,否则我们将当前区间最小值设为x即可。

对于所有操作2,因为给定了sigmaK<=5e6,所以我们可以直接寻找K次,每一次寻找了区间最小值后,将区间最小值改为INF,然后更新,再次询问,如果在K次询问内区间最小值大于X,那么就非法。每一次找到的最小值的信息我们用Vector储存,询问完成后再重新更新回去即可。

【代码~】

#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+10;
const int INF=0x3f3f3f3f;

pair<int,int> minn[MAXN<<2];
int tag[MAXN<<2];
int n,m,q;
int a[MAXN];

inline int Read()
{
	int i=0,f=1;
	char c;
	for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
	if(c=='-')
	  f=-1,c=getchar();
	for(;c>='0'&&c<='9';c=getchar())
	  i=(i<<3)+(i<<1)+c-'0';
	return i*f;
}

inline void sc(int x)
{
	if(x>=10)
	  sc(x/10);
	putchar(x%10+48);
}

inline void push_up(int k) 
{
	minn[k]=min(minn[k<<1],minn[k<<1|1]);
}

inline void push_now(int k,int val) 
{
	minn[k].first=max(minn[k].first,val);
}

inline void push_down(int k)
{
	push_now(k<<1,minn[k].first);
	push_now(k<<1|1,minn[k].first);
}

inline void build(int root,int l,int r)
{
	if(l==r)
	{
		minn[root].first=Read();
		minn[root].second=l;
		return ;
	}
	int mid=l+r>>1;
	build(root<<1,l,mid);
	build(root<<1|1,mid+1,r);
	push_up(root);
}

inline void update(int k,int l,int r,const int &ql,const int &qr,const int &key)
{
	if(minn[k].first>key)
	  return ;
	if(ql<=l&&r<=qr)
	  return push_now(k,key);
	push_down(k);
	int mid=(l+r)>>1;
	if(ql<=mid)
	  update(k<<1,l,mid,ql,qr,key);
	if(mid<qr)
	  update(k<<1|1,mid+1,r,ql,qr,key);
	push_up(k);
}

inline void modify(int k,int l,int r,const int &pos,const int &key)
{
	if(l==r)
	  return (void)(minn[k].first=key);
	push_down(k);
	int mid=(l+r)>>1;
	if(pos<=mid)
	  modify(k<<1,l,mid,pos,key);
	else 
	  modify(k<<1|1,mid+1,r,pos,key);
	push_up(k);
}

inline pair<int,int> querymin(int k,int l,int r,const int &ql,const int &qr)
{
	if(ql<=l&&r<=qr)
	  return minn[k];
	push_down(k);
	int mid=(l+r)>>1;
	if(mid<ql)
	  return querymin(k<<1|1,mid+1,r,ql,qr);
	if(qr<=mid)
	  return querymin(k<<1,l,mid,ql,qr);
	return min(querymin(k<<1,l,mid,ql,qr),querymin(k<<1|1,mid+1,r,ql,qr));
}

int main()
{
	n=Read();
	build(1,1,n);
	q=Read();
	while(q--)
	{
		int cz=Read(),l=Read(),r=Read(),x=Read();
		if(cz==1)
		  update(1,1,n,l,r,x);
		else
		{
			int k=Read();
			if(r-l+1<k)
			{
				puts("-1");
				continue;
			}
			vector<pair<int,int> > vec;
			bool flag1=true;
			for(int i=1;i<=k;++i)
			{
				pair<int,int> tmp=querymin(1,1,n,l,r);
				if(tmp.first>=x)
				  break;
				vec.push_back(tmp);
				modify(1,1,n,tmp.second,INF);
			}
			if(vec.size()==k)
			{
				for(int i=0;i<k;++i)
				  sc(vec[i].first),putchar(' ');
				puts("");
			}
			else
			  puts("-1");
			for(int i=0;i<vec.size();++i)
			  modify(1,1,n,vec[i].second,vec[i].first);
		}
	}
	return 0;
}

 

转载于:https://www.cnblogs.com/Ishtar/p/10010801.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值