【2018/10/12测试T3】数据结构

本文介绍了一种基于段树的数据结构应用案例,通过修改区间数值和查询特定条件下的子集,展示了解决复杂数据操作的有效方法。文章详细解析了算法流程,包括构建段树、修改区间最小值和查询区间最小K个数的步骤。

【题目】

传送门

题目描述:

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

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

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

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

输入格式:

第一行一个数字 n n n 表示数列长度,
第二行 n n n 个数字分别表示 a 1 a_1 a1 a n a_n an
第三行一个数字 m m m 表示操作次数,
接下来 m m m 行每行表示一次操作,
第一个数 o p op op 表示操作类型, o p op op 可能是 1 1 1 2 2 2
如果 o p = 1 op=1 op=1,后面有 L,R,X 三个正整数表示把区间 [L,R] 中比 X 小的数字全部改成 X
如果 o p = 2 op=2 op=2,后面有 L,R,X,K 四个正整数表示查询区间 [L,R] 中比 X 小的最小的 K 个数

输出格式:

对于每个 o p = 2 op=2 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 中的 KK 的总和不超过 5*10^6

~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,无特殊限制。


【分析】

这道题有一种偏暴力的算法

首先对于操作 1,直接修改区间最小值,若 Min<X,则将 Min 改为 X;否则就不变

对于操作 2,操作 K 次,每次都找出那个区间的最小值 Min,然后判断 MinX 的关系(即若 Min>=X 的话就该输出 -1),然后将那个位置上的数改为 inf(保证下一次找最小值的时候不会找到它),做完这 K 次操作后再把原来的值改回来

由于 K 的总和不超过 5*10^6,这样做复杂度是有保证的(但是常数有点大),在代码中加点读优、输优之类的还是能 A 掉这道题的(为了代码简洁我就没加了)


【代码】

#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define N 500005
#define inf (1ll<<31ll)-1
using namespace std;
int a[N],tag[N<<2];
pair<int,int>minn[N<<2];
inline void build(int root,int l,int r)
{
	if(l==r)
	{
		minn[root]=make_pair(a[l],l);
		return;
	}
	int mid=(l+r)>>1;
	build(root<<1,l,mid);
	build(root<<1|1,mid+1,r);
	minn[root]=min(minn[root<<1],minn[root<<1|1]);
}
inline void change(int root,int k)
{
	minn[root].first=max(minn[root].first,k);
	tag[root]=max(tag[root],k);
}
inline void pushdown(int root)
{
	change(root<<1,tag[root]);
	change(root<<1|1,tag[root]);
	tag[root]=0;
}
inline void modify(int root,int l,int r,int x,int y,int k)
{
	if(l>=x&&r<=y)
	{
		change(root,k);
		return;
	}
	int mid=(l+r)>>1;
	if(tag[root])  pushdown(root);
	if(x<=mid)  modify(root<<1,l,mid,x,y,k);
	if(y>mid)  modify(root<<1|1,mid+1,r,x,y,k);
	minn[root]=min(minn[root<<1],minn[root<<1|1]);
}
inline pair<int,int> ask(int root,int l,int r,int x,int y)
{
	if(l>=x&&r<=y)
	  return minn[root];
	int mid=(l+r)>>1;
	if(tag[root])  pushdown(root);
	pair<int,int>ans;ans.first=inf;
	if(x<=mid)  ans=min(ans,ask(root<<1,l,mid,x,y));
	if(y>mid)  ans=min(ans,ask(root<<1|1,mid+1,r,x,y));
	return ans;
}
inline void insert(int root,int l,int r,int p,int x)
{
	if(l==r)
	{
		minn[root]=make_pair(x,p);
		return;
	}
	int mid=(l+r)>>1;
	if(tag[root])  pushdown(root);
	if(p<=mid)  insert(root<<1,l,mid,p,x);
	else  insert(root<<1|1,mid+1,r,p,x);
	minn[root]=min(minn[root<<1],minn[root<<1|1]);
}
int main()
{
//	freopen("segtree.in","r",stdin);
//	freopen("segtree.out","w",stdout);
	int n,m,i,j;
	scanf("%d",&n);
	for(i=1;i<=n;++i)
	  scanf("%d",&a[i]);
	build(1,1,n);
	scanf("%d",&m);
	int s,l,r,x,k;
	for(i=1;i<=m;++i)
	{
		scanf("%d%d%d%d",&s,&l,&r,&x);
		if(s==1)  modify(1,1,n,l,r,x);
		else
		{
			scanf("%d",&k);
			bool flag=true;
			vector<pair<int,int> >ans;
			for(j=1;j<=k;++j)
			{
				pair<int,int>Min=ask(1,1,n,l,r);
				if(Min.first>=x)  {flag=false;break;}
				ans.push_back(Min);
				insert(1,1,n,Min.second,inf);
			}
			if(!flag)  printf("-1\n");
			else
			{
				for(j=0;j<ans.size();++j)
				  printf("%d ",ans[j].first);
				printf("\n");
			}
			for(j=0;j<ans.size();++j)
			  insert(1,1,n,ans[j].second,ans[j].first);
		}
	}
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值