[Ynoi2016] 镜中的昆虫

文章介绍了一种解决特定问题的算法,利用数据结构如ODT和时间维扩展来处理区间修改和查询不同数出现次数,适用于IT竞赛场景。

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

64MB,1.5s

题目描述

您正在欣赏 galgame 的 HS,然后游戏崩溃了,于是您只能做数据结构题了:

维护一个长为 \(n\) 的序列 \(a_i\),有 \(m\) 次操作。

  1. 将区间 \([l,r]\) 的值修改为 \(x\)

  2. 询问区间 \([l,r]\) 出现了多少种不同的数,也就是说同一个数出现多次只算一个。

输入格式

第一行两个整数 \(n,m\)

第二行 \(n\) 个整数表示 \(a_i\)

后面 \(m\) 行每行为 \(1\ l\ r\ x\) 或者 \(2\ l\ r\) ,分别表示修改和询问。

输出格式

对于每个询问,输出一个数表示答案。

样例 #1

样例输入 #1

5 5
1 2 3 4 5
2 1 5
1 2 3 4
2 1 5
2 3 3
2 2 4

样例输出 #1

5
3
1
1

\(1\leq n , m \leq 10^5\)\(1\leq a_i\leq 10^9\)

数颜色不带修的时候,维护每一种颜色上一次的出现地方 \(ls_i\),对着 \(r\) 扫描线,当 新扫到一个 \(a_i\) 时把树状数组中 \(ls_{a_i}\) 那个地方改掉,把 \(i\) 赋值为 1,询问时后缀查询。

还有一种理解方式:如果一个数 \(i\) 上一次出现在 \(ls\),那么数的时候也就是数有多少个满足 \(l\le i\le r,ls<i\) 的点对。

那么回到原题,只有区间覆盖,可以考虑用 ODT ,然后现在是某一个区间 \([l,r]\) 都是 \(c\),如果 \(c\) 的上一次出现在 ls,那么可以插一个在 \(( ls,l)\) 的价值为 1 的点,插一个在 \((r+1,r)\) 的价值为 -1 的店。

但是可能会把某一个点对删除,所以要增加时间维,在删去时插一个在这个时间把这个点删去的点。

那么在 \(t\) 时刻的询问 \(l,r\) 就是把时间维不超过 \(t\),第一位不超过 \(l\),第二维不超过 \(r\) 的店的价值之和就是颜色数,可以用 CDQ 统计。

然后发现你超时了?把 CDQ 的排序改成归并。

然后发现你 MLE 了? l,r,t 都不超过 1e6,可以压成一个 long long.

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
bool fir;
int n,m,c,ans[N],k,tr[N];
struct node{
	int l,r,w;
	bool operator<(const node&n)const{
		return l<n.l;
	}
};
struct hjhakioi{
	long long k;
	bool operator<(const hjhakioi&q)const{
		if((k>>20&1048575)^(q.k>>20&1048575))
			return (k>>20&1048575)<(q.k>>20&1048575);
		return (k>>60)-1&&(q.k>>60)==1;
	}
}qu[N*19];
set<node>s;
set<pair<int,int> >t[N<<1];
map<int,int>mp;
bool sec;
void upd(int x,int y)
{
	for(;x<=n;x+=x&-x)
		tr[x]+=y;
}
int ask(int x)
{
	int ret=0;
	for(;x;x-=x&-x)
		ret+=tr[x];
	return ret;
}
void query(int x,int y,int t,int op)
{
	qu[++k]=(hjhakioi){(1LL*x<<40LL)^(1LL*y<<20LL)^t^(op+1LL<<60)};
}
void add(int l,int r,int tm,int c)
{
	set<pair<int,int> >::iterator it=t[c].lower_bound(make_pair(l,r));
	if(it!=t[c].end())
	{
		int ls=1;
		if(it!=t[c].begin())
		{
			--it;
			ls=it->second+1;
			++it;
		}
		query(ls,it->first,tm,-1);
		query(r+1,it->first,tm,1);
	}
	int ls=1;
	if(it!=t[c].begin())
	{
		--it;
		ls=it->second+1;
	}
	query(ls,l,tm,1);
	query(r+1,r,tm,-1);
	t[c].insert(make_pair(l,r));
	s.insert((node){l,r,c});
}
void del(int l,int r,int tm,int c)
{
	set<pair<int,int> >::iterator it=t[c].upper_bound(make_pair(l,r)),i;
	if(it!=t[c].end())
	{
		int ls=1;
		i=it;
		query(r+1,it->first,tm,-1);
		--it;
		if(it!=t[c].begin())
		{
			--it;
			ls=it->second+1;
			++it;
		}
		++it;
		query(ls,it->first,tm,1);
	}
	--it;
	int ls=1;
	if(it!=t[c].begin())
	{
		--it;
		ls=it->second+1;
		++it;
	}
	query(ls,l,tm,-1);
	query(r+1,r,tm,1);
	t[c].erase(make_pair(l,r));
	s.erase((node){l,r,c});
}
set<node>::iterator split(int x,int t)
{
	set<node>::iterator it=s.lower_bound((node){x,0,0});
	if(it!=s.end()&&it->l==x)
		return it;
	--it;
	int l=it->l,r=it->r,w=it->w;
	del(l,r,t,w);
	add(l,x-1,t,w);
	add(x,r,t,w);
	return s.lower_bound((node){x,r,w});
}
void solve(int l,int r,int x,int y)
{
	if(x>y)
		return;
	if(l^r)
	{
		int md=l+r>>1,k=y+1;
		for(int i=x;i<=y;i++)
			if((qu[i].k&1048575)>md)
				k=i,i=y;
		solve(l,md,x,k-1);
		solve(md+1,r,k,y);
		inplace_merge(qu+x,qu+k,qu+y+1);
	}
	sort(qu+x,qu+y+1);
	int md=l+r>>1;
	for(int i=x;i<=y;i++)
	{
		if(1==(qu[i].k>>60)&&(qu[i].k&1048575)>md)
			ans[qu[i].k&1048575]+=ask(qu[i].k>>40&1048575);
		if(1^(qu[i].k>>60)&&(qu[i].k&1048575)<=md)
			upd(qu[i].k>>40&1048575,(qu[i].k>>60)-1);
	}
	for(int i=x;i<=y;i++)
		if(1^(qu[i].k>>60)&&(qu[i].k&1048575)<=md)
			upd(qu[i].k>>40&1048575,1-(qu[i].k>>60));
}
void write(int x)
{
	if(!x)
		return;
	write(x/10);
	putchar(x%10+48);
}
int read()
{
	int s=0;
	char ch=getchar();
	while(ch<'0'||ch>'9')
		ch=getchar();
	while(ch>='0'&&ch<='9')
		s=s*10+ch-48,ch=getchar();
	return s;
}
int main()
{
	memset(ans,-1,sizeof(ans));
	scanf("%d%d",&n,&m);
	for(int i=1,x;i<=n;i++)
	{
		if(!mp[x=read()])
			mp[x]=++c;
		add(i,i,0,mp[x]);
	}
	for(int i=1,op,l,r,x;i<=m;i++)
	{
		op=read(),l=read(),r=read();
		if(op^2)
		{
			if(!mp[x=read()])
				mp[x]=++c;
			set<node>::iterator itr=split(r+1,i),itl=split(l,i);
			for(set<node>::iterator it=itl,j;it!=itr;)
			{
				j=it;
				it++;
				del(j->l,j->r,i,j->w);
			}
			add(l,r,i,mp[x]);
		}
		else
			query(l,r,i,ans[i]=0);
	}
	solve(0,m,1,k);
	for(int i=1;i<=m;i++)
		if(~ans[i])
			write(ans[i]),puts("");
}
“51单片机通过MPU6050-DMP获取姿态角例程”解析 “51单片机通过MPU6050-DMP获取姿态角例程”是一个基于51系列单片机(一种常见的8位微控制器)的程序示例,用于读取MPU6050传感器的数据,并通过其内置的数字运动处理器(DMP)计算设备的姿态角(如倾斜角度、旋转角度等)。MPU6050是一款集成三轴加速度计和三轴陀螺仪的六自由度传感器,广泛应用于运动控制和姿态检测领域。该例程利用MPU6050的DMP功能,由DMP处理复杂的运动学算法,例如姿态融合,将加速度计和陀螺仪的数据进行整合,从而提供稳定且实时的姿态估计,减轻主控MCU的计算负担。最终,姿态角数据通过LCD1602显示屏以字符形式可视化展示,为用户提供直观的反馈。 从标签“51单片机 6050”可知,该项目主要涉及51单片机和MPU6050传感器这两个关键硬件组件。51单片机基于8051内核,因编程简单、成本低而被广泛应用;MPU6050作为惯性测量单元(IMU),可测量设备的线性和角速度。文件名“51-DMP-NET”可能表示这是一个与51单片机及DMP相关的网络资源或代码库,其中可能包含C语言等适合51单片机的编程语言的源代码、配置文件、用户手册、示例程序,以及可能的调试工具或IDE项目文件。 实现该项目需以下步骤:首先是硬件连接,将51单片机与MPU6050通过I2C接口正确连接,同时将LCD1602连接到51单片机的串行数据线和控制线上;接着是初始化设置,配置51单片机的I/O端口,初始化I2C通信协议,设置MPU6050的工作模式和数据输出速率;然后是DMP配置,启用MPU6050的DMP功能,加载预编译的DMP固件,并设置DMP输出数据的中断;之后是数据读取,通过中断服务程序从DMP接收姿态角数据,数据通常以四元数或欧拉角形式呈现;再接着是数据显示,将姿态角数据转换为可读的度数格
MathorCup高校数学建模挑战赛是一项旨在提升学生数学应用、创新和团队协作能力的年度竞赛。参赛团队需在规定时间内解决实际问,运用数学建模方法进行分析并提出解决方案。2021年第十一届比赛的D就是一个典型例子。 MATLAB是解决这类问的常用工具。它是一款强大的数值计算和编程软件,广泛应用于数学建模、数据分析和科学计算。MATLAB拥有丰富的函数库,涵盖线性代数、统计分析、优化算法、信号处理等多种数学操作,方便参赛者构建模型和实现算法。 在提供的文件列表中,有几个关键文件: d论文(1).docx:这可能是参赛队伍对D的解答报告,详细记录了他们对问的理解、建模过程、求解方法和结果分析。 D_1.m、ratio.m、importfile.m、Untitled.m、changf.m、pailiezuhe.m、huitu.m:这些是MATLAB源代码文件,每个文件可能对应一个特定的计算步骤或功能。例如: D_1.m 可能是主要的建模代码; ratio.m 可能用于计算某种比例或比率; importfile.m 可能用于导入数据; Untitled.m 可能是未命名的脚本,包含临时或测试代码; changf.m 可能涉及函数变换; pailiezuhe.m 可能与矩阵的排列组合相关; huitu.m 可能用于绘制回路图或流程图。 matlab111.mat:这是一个MATLAB数据文件,存储了变量或矩阵等数据,可能用于后续计算或分析。 D-date.mat:这个文件可能包含与D相关的特定日期数据,或是模拟过程中用到的时间序列数据。 从这些文件可以推测,参赛队伍可能利用MATLAB完成了数据预处理、模型构建、数值模拟和结果可视化等一系列工作。然而,具体的建模细节和解决方案需要查看解压后的文件内容才能深入了解。 在数学建模过程中,团队需深入理解问本质,选择合适的数学模
以下是关于三种绘制云图或等高线图算法的介绍: 一、点距离反比插值算法 该算法的核心思想是基于已知数据点的值,计算未知点的值。它认为未知点的值与周围已知点的值相关,且这种关系与距离呈反比。即距离未知点越近的已知点,对未知点值的影响越大。具体来说,先确定未知点周围若干个已知数据点,计算这些已知点到未知点的距离,然后根据距离的倒数对已知点的值进行加权求和,最终得到未知点的值。这种方法简单直观,适用于数据点分布相对均匀的情况,能较好地反映数据在空间上的变化趋势。 二、双线性插值算法 这种算法主要用于处理二维数据的插值问。它首先将数据点所在的区域划分为一个个小的矩形单元。当需要计算某个未知点的值时,先找到该点所在的矩形单元,然后利用矩形单元四个顶点的已知值进行插值计算。具体过程是先在矩形单元的一对对边上分别进行线性插值,得到两个中间值,再对这两个中间值进行线性插值,最终得到未知点的值。双线性插值能够较为平滑地过渡数据值,特别适合处理图像缩放、地理数据等二维场景中的插值问,能有效避免插值结果出现明显的突变。 三、面距离反比 + 双线性插值算法 这是一种结合了面距离反比和双线性插值两种方法的算法。它既考虑了数据点所在平面区域对未知点值的影响,又利用了双线性插值的平滑特性。在计算未知点的值时,先根据面距离反比的思想,确定与未知点所在平面区域相关的已知数据点集合,这些点对该平面区域的值有较大影响。然后在这些已知点构成的区域内,采用双线性插值的方法进行进一步的插值计算。这种方法综合了两种算法的优点,既能够较好地反映数据在空间上的整体分布情况,又能保证插值结果的平滑性,适用于对插值精度和数据平滑性要求较高的复杂场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值