2023NOIP A层联测39 草莓列车

文章讨论了一个编程问题,涉及对序列进行多次操作,每次操作改变某个区间的值为最大值。通过优化,将区间修改转化为单点修改并结合分块技术,降低了时间复杂度,从而避免超时。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

给定一个序列aia_iai,有mmm次操作,形如l r v,表示将[l,r][l,r][l,r]的每个aia_iai变为max⁡(ai,v)\max(a_i,v)max(ai,v)。求最终的序列。

为了避免过量的输入,此题使用特殊的读入格式。

第一行有三个整数n,m,typen,m,typen,m,type,其中typetypetype表示数据类型。

type=1type=1type=1时,每次操作的区间都满足左端点l=1l=1l=1

第二行有nnn个数,表示初始序列。

第三行有两个数x0,seedx_0,seedx0,seed,使用示例如下:

namespace Maker{
	unsigned int x0,seed;
	void init(){scanf("%u%u",&x0,&seed);}
	inline unsigned int getnum(){
		x0=(x0<<3)^x0;
		x0=((x0>>5)+seed)^x0;
		return x0;
	}
}
int n,m,typ;
int main(){
	scanf("%d%d%d",&n,&m,&typ);
	// input n numbers as a[1...n]
	Maker::init();
	for(int i=1; i<=m; ++i){
		int l=Maker::getnum()%n+1,r=Maker::getnum()%n+1;
		unsigned int v=Maker::getnum();
		if(l>r) swap(l,r);
		if(typ==1) l=1;
 		// do something
 	}
	// output n numbers of a[1...n]
}

数据范围

1≤n≤105,1≤m≤1071\leq n\leq 10^5,1\leq m\leq 10^71n105,1m107


题解

对于每次操作l r v,我们将rrrvvv存储在维护区间左端点lllvectorvectorvector中。

然后,从左到右枚举左端点iii。对于每个以iii为左端点的操作,设这个操作的右端点为rrr,权值为vvv,则将rrr及其左边的部分全部对vvvmax⁡\maxmax,然后查询位置iii的值。这里涉及区间修改和单点查询。

区间修改的次数为O(m)O(m)O(m),单点查询的次数为O(n)O(n)O(n),用线段树的话是O((m+n)log⁡n)O((m+n)\log n)O((m+n)logn),会TLE\text{TLE}TLE。下面考虑优化。

因为区间修改的次数比单点查询的次数大很多,而且区间修改的速度肯定不快于单点查询,所以我们可以将区间修改变成对rrr的单点修改,将单点查询变成查询iiinnn的最大值,那么现在要求的操作就是单点修改和区间查询。

考虑分块,单点修改是O(1)O(1)O(1)的,区间查询是O(n)O(\sqrt n)O(n)的,那么总时间复杂度为O(m+nn)O(m+n\sqrt n)O(m+nn),是可以过的。

code

#include<bits/stdc++.h>
using namespace std;
namespace Maker{
	unsigned int x0,seed;
	void init(){scanf("%u%u",&x0,&seed);}
	inline unsigned int getnum(){
		x0=(x0<<3)^x0;
		x0=((x0>>5)+seed)^x0;
		return x0;
	}
}
const int N=100000,B=350;
int n,m,typ,bl;
unsigned int a[N+5],w[N+5],tw[B+5];
struct node{
	int x;
	unsigned int v;
};
vector<node>p[N+5];
int pos(int i){
	return (i-1)/bl+1;
}
void ch(int x,unsigned int v){
	w[x]=max(w[x],v);
	tw[pos(x)]=max(tw[pos(x)],v);
}
unsigned int find(int l,int r){
	int vl=pos(l),vr=pos(r);
	unsigned int re=0;
	if(vl==vr){
		for(int i=l;i<=r;i++) re=max(re,w[i]);
		return re;
	}
	for(int i=l;i<=vl*bl;i++) re=max(re,w[i]);
	for(int i=vl+1;i<=vr-1;i++) re=max(re,tw[i]);
	for(int i=vr*bl-bl+1;i<=r;i++) re=max(re,w[i]);
	return re;
}
int main()
{
//	freopen("train.in","r",stdin);
//	freopen("train.out","w",stdout);
	scanf("%d%d%d",&n,&m,&typ);bl=sqrt(n);
	for(int i=1;i<=n;i++) scanf("%u",&a[i]);
	Maker::init();
	for(int i=1;i<=m;++i){
		int l=Maker::getnum()%n+1,r=Maker::getnum()%n+1;
		unsigned int v=Maker::getnum();
		if(l>r) swap(l,r);
		if(typ==1) l=1;
		p[l].push_back((node){r,v});
	}
	for(int i=1;i<=n;i++){
		for(node j:p[i]) ch(j.x,j.v);
		a[i]=max(a[i],find(i,n));
		printf("%u ",a[i]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值