首页 > 其他分享 >回滚莫队 学习笔记

回滚莫队 学习笔记

时间:2023-08-23 22:14:09浏览次数:32  
标签:回滚 int 笔记 100005 add 端点 include 莫队

板子题交 \(998244353\) 遍一直 UKE 我哭死。

回滚莫队

有些题看起来像个莫队,想着想着发现 add 操作很容易实现,而 del 操作怎么都想不出来,或者是 del 操作时间复杂度不是 \(O(1)\) 时间复杂度爆炸,那么回滚莫队就能派上用场。这种莫队不带删因此也叫做不带删莫队。

AT_joisc2014_c 歴史の研究

给你一个长度为 \(n\) 的数组 \(A\) 和 \(m\) 个询问 \((1 \leq n, m \leq 10^5)\),每次询问一个区间 \([L, R]\) 内重要度最大的数字,要求输出其重要度。一个数字 \(i\) 重要度的定义为 \(i\) 乘上 \(i\) 在区间内出现的次数。

sol:

从莫队的角度出发,首先我们尝试实现 add 和 del 函数。

add 函数:在当前区间内增加一个数并更新答案。加入数 \(x\) 后令 \(cnt_x \leftarrow cnt_x + 1\) 然后更新答案即可。

del 函数:?好像很难 \(O(1)\) 实现。因为删除最大值后好像没法快速找回次小值。

那怎么办?不要删除操作就行了。

首先还是很正常的莫队分块+排序。

然后按顺序处理询问:

  • 如果询问的左端点跳到了新的一块,那么将左右指针扔到当前块的右边形成空集。

  • 如果询问的左右端点在同一块内,直接暴力即可。

  • 往右跳右指针到右端点。

  • 然后往左跳左指针到左端点。

  • 到位后回答询问。

  • 撤销之前的修改,把指针挪回去。

注意这里的撤销并不是删除,撤销的方法很多,例如这题就是直接记录原位的答案,然后再把 \(cnt\) 一路滚回去。

这样我们就做到了不带删除完成莫队的内容。

时间复杂度方面,不是很会证明。设询问数量是 \(q\) 且与 \(n\) 同阶,我的感性理解就是左端点一直只在一块内扫,变化量在块长 \(T\) 以内,变化次数与块的个数和询问个数有关当块长取根号级别的而右端点往右扫所以是个 \(O(n)\) 。块长取 \(\large\frac{n}{\sqrt{q}}\) 是最优的,时间复杂度 \(n \sqrt{q}\),当然如果 \(n\) 和 \(q\) 不同阶那还需要再仔细调整下块长。

code:

#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
int n, Q, N, T, bl, tmp;
int a[100005], b[100005];
int L[100005], R[100005];
int belong[100005];
int ans[100005];
struct Query{
	int l, r, id;
}q[100005];
bool cmp(Query u, Query v){
	return belong[u.l] == belong[v.l] ? u.r < v.r : u.l < v.l;
}
int cn[100005], cnt[100005];
int solve(int l, int r){
	int ans = 0;
	for(int i = l; i <= r; i ++)
		cn[a[i]] ++;
	for(int i = l; i <= r; i ++)
		ans = max(ans, cn[a[i]] * b[a[i]]);
	for(int i = l; i <= r; i ++)
		cn[a[i]] --;
	return ans;
}
int add(int x){
	cnt[a[x]] ++;
	tmp = max(tmp, cnt[a[x]] * b[a[x]]);
	return 0;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0); 
	cin >> n >> Q;
	for(int i = 1; i <= n; i ++)
		cin >> a[i], b[i] = a[i];
	for(int i = 1; i <= Q; i ++)
		cin >> q[i].l >> q[i].r, q[i].id = i;
	sort(b + 1, b + n + 1);
	N = unique(b + 1, b + n + 1) - b - 1;
	for(int i = 1; i <= n; i ++)
		a[i] = lower_bound(b + 1, b + N + 1, a[i]) - b;
	T = sqrt(n), bl = n / T;
	for(int i = 1; i <= n; i ++)
		L[i] = R[i - 1] + 1, R[i] = L[i] + T - 1;
	if(R[bl] < n) ++ bl, L[bl] = R[bl - 1] + 1, R[bl] = n;
	for(int i = 1; i <= n; i ++)
		belong[i] = (i - 1) / T + 1;
	sort(q + 1, q + Q + 1, cmp);
	for(int i = 1, r = 0, l = 0, nw = 1; i <= bl; i ++){
		memset(cnt, 0, sizeof(cnt));
		r = R[i];
		tmp = 0;
		while(belong[q[nw].l] == i){
			l = R[i] + 1;
			if(q[nw].r - q[nw].l <= T){
				ans[q[nw].id] = solve(q[nw].l, q[nw].r);
				++ nw;
				continue;
			}
			while(q[nw].r > r) add(++ r);
			int ret = tmp;
			while(l > q[nw].l)
				add(-- l);
			ans[q[nw].id] = tmp;
			tmp = ret;
			while(l <= R[i])
				cnt[a[l ++]] --;
			++ nw;
		}
	}
	for(int i = 1; i <= Q; i ++)
		cout << ans[i] << "\n";
	return 0;
}

另外一直在 UKE 我也不知道对不对反正样例是过了。

标签:回滚,int,笔记,100005,add,端点,include,莫队
From: https://www.cnblogs.com/AzusidNya/p/17652889.html

相关文章

  • [刷题笔记] Luogu P2285 [HNOI2004] 打鼹鼠
    ProblemAnalysis我们初始可以任意决定机器人的位置,状态很多,暴力显然会寄掉。不妨先贪心的思考一下。我们肯定希望机器人初始在最先出现鼹鼠的洞,因为出现在没有鼹鼠的洞是无效的。题目保证输入数据是严格按照出现时间递增顺序给出。定义\(f_i\)表示前\(i\)只鼹鼠最多能打到......
  • 树链剖分学习(复习?)笔记
    树链剖分,即树剖。顾名思义,树链剖分就是将一棵树通过某种方式剖分成若干条链,再利用\(dfs\)序,从而将树上的问题转化为序列上的问题。树剖的方式有不止一种,比如重链剖分、长链剖分。最常用的(大概?)是重链剖分。此处介绍重链剖分。首先,我们定义一个节点的重儿子为此节点的所有儿......
  • [刷题笔记] Luogu P4933 大师
    ProblemDescription给定一个长度为\(n\)的数组\(h\),你可以从中选取若干数字,使得你选择的数组组成一个等差数列。特别地,单一的数字和只有两个数字也算作等差数列。求你可选择的方案数。答案对\(998244353\)取模。Analysis考虑\(f_{i,j}\)表示前\(i\)个数,公差为\(j\)......
  • C++笔记
    C++笔记将数字以十六进制输出:cout<<hex<<100<<endl;将数字以八进制输出:cout<<oct<<100<<endl;精度控制include保存a位小数:setprecision(a)将b保留a位:cout<<setprecision(a)<<b<<endl将b保留小数点后a位:cout<<setiosflags(ios::fixed)<<se......
  • [刷题笔记] Luogu P1064 [NOIP2006 提高组] 金明的预算方案
    ProblemAnalysis我们发现如果忽略主从关系,那这道题就是一个裸的01背包问题。主从关系处理也非常简单,借鉴P2014选课的经验,转换成树上背包问题。同理,本题是一个森林,若将0号节点参与建树的话就可以把森林转换成树,处理方便。具体地,设\(f_{i,j}\)表示以\(i\)为父节点,剩......
  • 学习笔记:什么是Wasserstein distance
    简单地说,就是衡量两个概率分布之间的差异。也可以说是将一个概率分布转换成另一个概率分布要花费多少代价。图1:在一维空间中的三个概率分布比如,上图中有三个概率分布f,g,h,我们可以说f与g之间的距离比f与h之间的距离更小。上述只是感性上的认知,那么如何计算出准确......
  • C++面向对象笔记(转载自黑马程序员)
    C++核心编程本阶段主要针对C++面向对象编程技术做详细讲解,探讨C++中的核心和精髓。1内存分区模型C++程序在执行时,将内存大方向划分为4个区域代码区:存放函数体的二进制代码,由操作系统进行管理的全局区:存放全局变量和静态变量以及常量栈区:由编译器自动分配释放,存放函数的......
  • openGauss学习笔记-48 openGauss 高级数据管理-函数
    openGauss学习笔记-48openGauss高级数据管理-函数openGauss常用的函数如下:48.1数学函数abs(x)描述:绝对值。返回值类型:和输入相同。示例:openGauss=#SELECTabs(-17.4);abs------17.4(1row)cbrt(dp)描述:立方根。返回值类型:doubleprecision示例:openGauss......
  • 《408操作系统 》复习笔记 ③ 第二章 调度与调度算法
    调度当有一堆任务要处理,由于资源有限,没办法同时处理。需要某种规则来决定处理这些任务的顺序作业作业:一个具体的任务用户向系统提交一个作业=用户让操作系统启动一个程序(来处理一个具体的任务)调度的三个层次高级调度(作业调度)按照某种策略从外存的作业后备队列中挑选......
  • Asp.net mvc 笔记
    捕捉处理全局异常自定义一个Attribute继承默认的HandleErrorAttributenamespaceEmpowerApiService.Filter{publicclassCustomerErrorAttribute:HandleErrorAttribute{privatestaticNLog.Loggerlogger=NLog.LogManager.GetCurrentClassLogger();......