暴力算法 --- 莫队

news2024/11/25 0:29:54

文章目录

      • 莫队
        • 基础莫队
        • 带修改莫队
        • 树上莫队
        • 回滚莫队

莫队

什么是莫队?

答:优雅的暴力!!!

基础莫队

重复的数

题目描述:给出一个长度为 N N N的序列,有若干查询,每次查询区间 [ l i , r i ] [l_i, r_i] [li,ri]出现 k i k_i ki次的数有多少个?

解题思路:最直观的做法就是拿到区间 [ l , r ] [l, r] [l,r]后,暴力计算,用 c n t [ x ] cnt[x] cnt[x]保存 x x x出现了几个, p o s [ t ] pos[t] pos[t]出现 t t t次的数有几个。此时,时间复杂度最坏为: O ( n 2 ) \mathcal O(n^2) O(n2)
可以尝试来优化一下这个操作:使用两个指针 L L L R R R(初始化: L = 1 , R = 0 L = 1, R = 0 L=1,R=0),对于每次查询,依次的把这两个指针移动到目标区间 [ l , r ] [l, r] [l,r]的同时,更新 c n t cnt cnt p o s pos pos数组。假设现在目标区间为: [ 1 , 5 ] [1, 5] [1,5] L = 2 , R = 5 L = 2, R = 5 L=2,R=5;移动 L L L指针左移L--,并更新 c n t cnt cnt p o s pos pos数组:

L --;
pos[cnt[a[L]]] --;
cnt[a[L]] ++;
pos[cnt[a[L]]] ++;

移完以后,同理移动 R R R指针:

R ++;
pos[cnt[a[R]]] --;
cnt[a[R]] ++;
pos[cnt[a[R]]] ++;

可以发现上述都是把不在指针区间 [ L , R ] [L, R] [L,R]的数加进来了,因此可以封装成一个函数:

void add(int x) { // 把一个数x加入当指针区间内
    pos[cnt[x]] --;
    cnt[x] ++;
    pos[cnt[x]] ++;
}

有增加就有删除,删除也是一样的:

void del(int x) { // 把一个数从指针区间删除
    pos[cnt[x]] ++;
    cnt[x] --;
    pos[cnt[x]] --;
}

为了减少 [ L , R ] [L, R] [L,R]这两个指针的移动次数,可以先将序列分成 n \sqrt n n 块,并从 1 ∼ n 1 \sim \sqrt n 1n 编号,对于所有的查询按照块编号,从小到大排序,对于同一块,按照区间右端点升序排序。排完序后,我们再按照上述的操作去移动 L , R L, R L,R指针,这样的时间复杂度为: O ( n n ) \mathcal O(n\sqrt n) O(nn )

简单证明一下:

  1. 区间排序,sort一遍, O ( n l o g n ) \mathcal O(n log n) O(nlogn)
  2. L L L指针的移动:对于每块最坏跨越整个块 n \sqrt n n ,最坏有 n n n块需要移动,总的: O ( n n ) \mathcal O(n\sqrt n) O(nn )
  3. R R R指针的移动,因为对于每块,区间是按照右端点升序排序,所以在块内最多也移动 n \sqrt n n ,最坏有 n n n块需要移动,总的 O ( n n ) \mathcal O(n \sqrt n) O(nn )

综上述:莫队的总时间复杂度为: O ( n l o g n ) + O ( n n ) + O ( n n ) = O ( n n ) \mathcal O(nlogn) + \mathcal O (n \sqrt n) + \mathcal O(n \sqrt n) = \mathcal O(n \sqrt n) O(nlogn)+O(nn )+O(nn )=O(nn )

AC_Code:

#include <bits/stdc++.h>
using namespace std;
constexpr int N = 1e5 + 10;
int n, len, m, a[N], cnt[N], pos[N];
int bel(int x) {
	return x / len;
}
struct query {
	int l, r, k, id;
	bool operator < (const query &b){
		return bel(l) == bel(b.l) ? r < b.r : bel(l) < bel(b.l);
	}
};

void add(int x) {
	pos[cnt[x]] --; 
	cnt[x] ++;
	pos[cnt[x]] ++;
}

void del(int x) {
	pos[cnt[x]] --;
	cnt[x] --;
	pos[cnt[x]] ++;
}

int main() {
	scanf("%d", &n); len = sqrt(n);
	for (int i = 1; i <= n; i ++) scanf("%d", &a[i]);
	scanf("%d", &m);
	vector<query> q(m);
	vector<int> ans(m);
	for (int i = 0; i < m; i ++) {
		q[i].id = i;
		cin >> q[i].l >> q[i].r >> q[i].k;
	}
	sort(q.begin(), q.end());
	for (int i = 0, l = 1, r = 0; i < m; i ++) {
		int ql = q[i].l, qr = q[i].r;
		while (l > ql) add(a[-- l]);
		while (r < qr) add(a[++ r]);
		while (l < ql) del(a[l ++]);
		while (r > qr) del(a[r --]);
		ans[q[i].id] = pos[q[i].k];
	}
	for (int i = 0; i < m; i ++) cout << ans[i] << "\n";
}

带修改莫队

P1903 [国家集训队] 数颜色 / 维护队列

题目描述:有长度为 N N N的序列,现在有两个操作:

  1. Q   L   R Q \ L \ R Q L R 区间 [ L , R ] [L, R] [L,R]有多少个不同的数
  2. R   P   C R \ P \ C R P C P P P位置的数改成 C C C

解题思路:查询操作可以用莫队轻松解决,可是这个修改要怎么办呢?我们可以加上一维时间,使之变为待修改莫队!具体操作如下:

增加一维时间t,在把区间移动到目标区间后,检查当前时间是不是目标时间,不是的话,跟修改 L , R L, R L,R指针一样去修改时间t。

这里要注意的是,比如你现在把某个位置的数 x x x改成了 c c c,此时的时间为 t 1 t_1 t1,在把 x x x修改为 c c c后,这个时候你要把原来的数 x x x记下来,如果以后询问小于 t 1 t_1 t1的时候,这个位置的数是 x x x而不是 c c c,这里有个简化操作,可以直接把 x x x c c c互换即可,下次操作直接更改就行(具体细节看代码

AC_Code:

#include <bits/stdc++.h>
using namespace std;

constexpr int N = 1e6 + 10;
int n, m, len, a[N], cnt[N];

int bel(int x) {
	return x / len;
}

struct query {
	int l, r, t, id;
	bool operator < (const query & b) {
		return (bel(l) ^ bel(b.l)) ? bel(l) < bel(b.l) : 
									(bel(r) ^ bel(b.r)) ? bel(r) < bel(b.r) : t < b.t; 
	}
};
struct M {
	int pos, color;
};

M modify[N];
int now;
void add(int x) {
	if(!cnt[x]) ++ now;
	cnt[x] ++;
}
void del(int x) {
	cnt[x] --;
	if(!cnt[x]) -- now;
}
int main() {
	scanf("%d%d", &n, &m); 
	//len = sqrt(n); 此时退化为了O(n^2)
	len = pow(n, 2.0 / 3);
	for (int i = 1; i <= n; i ++) scanf("%d", &a[i]);
	vector<query> q; int cntc = 0;
	for (int i = 0; i < m; i ++) {
		char opt[2]; int l, r;
		scanf("%s%d%d", opt, &l, &r);
		if(opt[0] == 'Q') {
			q.push_back({l, r, cntc, (int)q.size()});
		} else {
			modify[++cntc] = {l, r};
		}
	}
	sort(q.begin(), q.end());
	vector<int> ans(q.size());
	for (int i = 0, l = 1, r = 0, t = 0; i < (int)q.size(); i ++) {
		int ql = q[i].l, qr = q[i].r, qt = q[i].t;
		while (l > ql) add(a[-- l]);
		while (r < qr) add(a[++ r]);
		while (l < ql) del(a[l ++]);
		while (r > qr) del(a[r --]);
		while (t < qt) {
			++ t;
			if(ql <= modify[t].pos && qr >= modify[t].pos) {
				del(a[modify[t].pos]);
				add(modify[t].color);
			}
			swap(a[modify[t].pos], modify[t].color);
		}
		while (t > qt) {
			if(ql <= modify[t].pos && qr >= modify[t].pos) {
				del(a[modify[t].pos]);
				add(modify[t].color);
			}
			swap(a[modify[t].pos], modify[t].color);
			-- t;
		}
		ans[q[i].id] = now;
	}
	for (int &x : ans) cout << x << "\n";
}

树上莫队

COT2 - Count on a tree II

题目描述:给定 N N N个节点的树,每个节点有一个颜色。现在有 m m m次询问,每次询问给出两个节点 u , v u,v u,v,输出 u , v u,v u,v路径上的节点颜色个数。颜色为 ≤ 2 × 1 0 9 \le 2 × 10^9 2×109的非负整数

解题思路

先得到树的欧拉序。

欧拉序特点:

每个节点出现两次,且他们中间的节点为这个节点子树上的点

欧拉序的求法:

在dfs的时候,得到一个点加入序列,退出的时候也加入序列,这样就得到了欧拉序

得到欧拉序有什么用呢,可以尝试将两个节点映射成欧拉序上的一段区间,这样问题就可以转换为莫队求区间不同数了
在这里插入图片描述

看上图,1->3很容易得到(就是图中红线)而且之间出现两次的数刚好不会算在其中。但是4->3怎么算?用第一个4指定不行,只能用第二个4,但是我们发现少了1,但是1是什么呢,是4和3的lca!所以,可以得到下面两个结论!(保证 f i r s t [ x ] < f i r s t [ y ] first[x] < first[y] first[x]<first[y],不满足就交换一下)

  1. 如果 L C A ( x , y ) = = x LCA(x, y) == x LCA(x,y)==x,区间为 [ f i r s t [ x ] , f i r s t [ y ] [first[x], first[y] [first[x],first[y] 对应上图1->3情况
  2. 如果 L C A ( x , y )   ! = x LCA(x, y) \ != x LCA(x,y) !=x,区间为 [ s e c o n d [ x ] , f i r s t [ y ] ] [second[x], first[y]] [second[x],first[y]],同时标记lca为 L C A ( x , y ) LCA(x, y) LCA(x,y),在计算的时候加上lca

AC_Code:

#include <bits/stdc++.h>
using namespace std;
template < typename T>
inline void read( T &x)
{
    x = 0; bool f = 0;
    char ch = getchar();
    while(!isdigit(ch)) { f ^= !(ch ^ 45), ch = getchar();}
    while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
    x = f ? -x : x;
}
template < typename T>
inline void write(T x)
{
    if(x < 0) putchar('-'), x = -x;
    if(x > 9) write(x / 10);
    putchar(x % 10 + 48);
}
template <typename T>
inline void write(T x, char ch) {
	write(x);
	putchar(ch);
}
constexpr int N = 2e5 + 10;
int n, m, len, a[N], cnt[N], ord[N], first[N], last[N], ncnt, depth[N], fa[N][30];
vector<int> G[N];

int bel(int x) {
	return x / len;
}

struct query {
	int l, r, lca, id;
	bool operator < (const query & b) {
		return (bel(l) ^ bel(b.l)) ? bel(l) < bel(b.l) : r < b.r;
	}
};

void dfs(int u) {
	ord[++ ncnt] = u;
	first[u] = ncnt;
	for (int &x : G[u]) {
		if(x == fa[u][0]) continue;
		depth[x] = depth[u] + 1;
		fa[x][0] = u;
		for (int i = 1; (1 << i) <= depth[x]; i ++)
			fa[x][i] = fa[fa[x][i - 1]][i - 1];
		dfs(x);
	}
	ord[++ ncnt] = u;
	last[u] = ncnt;
}

int LCA(int u, int v) {
	if(depth[u] < depth[v]) swap(u, v);
	for (int i = 20; i + 1;i --)
		if(depth[u] - (1 << i) >= depth[v]) u = fa[u][i];
	if(u == v) return u;
	for (int i = 20; i + 1; i --) 
		if(fa[u][i] != fa[v][i])
			u = fa[u][i], v = fa[v][i];
	return fa[u][0];
}

int vis[N], now;
vector<int> all;

void solve(int pos) {
	if(vis[pos]) {
		cnt[a[pos]] --;
		if(!cnt[a[pos]]) now --;
	} else {
		if(!cnt[a[pos]]) now ++;
		cnt[a[pos]] ++;
	}
	vis[pos] ^= 1;
}

int main() {
	read(n); read(m);
	for (int i = 1; i <= n; i ++)
		read(a[i]), all.push_back(a[i]);
	sort(all.begin(), all.end());
	all.erase(unique(all.begin(), all.end()), all.end());
	for (int i = 1; i <= n; i ++)
		a[i] = lower_bound(all.begin(), all.end(), a[i]) - all.begin() + 1;
	for (int i = 1; i < n; i ++) {
		int u, v;
		read(u); read(v);
		G[u].push_back(v); G[v].push_back(u);
	}
	depth[1] = 1;
	dfs(1);
	len = sqrt(ncnt);
	vector<query> q(m);
	for (int i = 0; i < m; i ++) {
		int l, r;
		read(l); read(r);
		int lca = LCA(l, r);
		if(first[l] > first[r]) swap(l, r);
		if(l == lca) {
			q[i].l = first[l];
			q[i].r = first[r];
		} else {
			q[i].l = last[l];
			q[i].r = first[r];
			q[i].lca = lca;
		}
		q[i].id = i;
	}
	sort(q.begin(), q.end());
	vector<int>ans(m);
	for (int i = 0, l = 1, r = 0; i < m; i ++) {
		int ql = q[i].l, qr = q[i].r, lca = q[i].lca;
		while (l > ql) solve(ord[-- l]);
		while (r < qr) solve(ord[++ r]);
		while (l < ql) solve(ord[l ++]);
		while (r > qr) solve(ord[r --]);
		if(lca) solve(lca);
		ans[q[i].id] = now;
		if(lca) solve(lca);
	}
	for (int &x : ans) write(x, '\n');
}

回滚莫队

// TODO 待补

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/58186.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网页信息抓取-网页信息采集器

抓取整个网页&#xff0c;我们怎么抓取整个网页自己想要的文章内容&#xff0c;很多人一想到内容抓取。就想到要学习一门编程语言&#xff0c;最让大家熟知的就是python爬虫。如果完全靠自己自学&#xff0c;又是从零基础开始学习Python的情况下&#xff0c;我认为至少需要半年…

Spring Boot 2 (六):使用 Docker 部署 Spring Boot 开源软件云收藏

云收藏项目已经开源2年多了,作为当初刚开始学习 Spring Boot 的练手项目,使用了很多当时很新的技术,现在看来其实很多新技术是没有必要使用的,但做为学习案例来讲确实是一个绝佳的 Spring Boot 实践。 从开源到现在,写了一些教程给大家介绍如何部署云收藏,如何在IDE中运…

[附源码]计算机毕业设计ssm校园二手交易平台

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

解决JSP中Bean在页面显示不正确问题(scope关键字)

问题出现 有一天我在编写JSP的程序时&#xff0c;在Java后端写了跳转并且传输数据语句&#xff0c;但前端界面渲染出来的数据却是我在DAO中初始化的数据。 第一句语句将book对象注入request的Session中&#xff0c;第二句实现跳转到JSP页面&#xff0c;第三句将此时的request和…

Logistic回归——银行违约情况分析

文章目录一、Logistic回归原理1、Sigmoid函数作用2、用梯度下降法求解参数二、利用Logistic回归分类1、数据预处理2、利用梯度上升计算回归系数3、训练和验证总结一、Logistic回归原理 1、Sigmoid函数作用 Logistic回归的原理是用逻辑函数把线性回归的结果(-∞,∞)映射到(0,1…

【408篇】C语言笔记-第十章(线性表)

文章目录第一节&#xff1a;线性表的顺序表示线性表1. 定义2. 特点线性表的顺序表示1. 顺序表2. 优缺点比较3. 插入操作4. 删除操作5. 动态分配代码示例第二节&#xff1a;线性表的链式表示单链表优缺点对比插入操作删除操作代码示例第一节&#xff1a;线性表的顺序表示 线性表…

python中的序列(列表、元组、字符串)的切片操作

目录 一.序列 二. 序列常用操作——切片 注意 演示 一.序列 序列是指:内容连续、有序&#xff0c;可使用下标索引的一类数据容器 列表、元组、字符串&#xff0c;均可以可以视为序列。 二. 序列常用操作——切片 序列支持切片&#xff0c;即:列表、元组、字符串&#xf…

看卡塔尔世界杯,diff一下足球比赛与软件开发

diff一下足球比赛与软件开发吐槽世界杯E组&#xff01;类比软件开发与足球比赛教练与架构师的作用新技术——半自动越位技术世界杯冠军吐槽世界杯E组&#xff01; 最近博主看了多场世界杯比赛&#xff0c;看的我心力交瘁&#xff0c;欲哭无泪。 从日本与哥斯达黎加那场比赛开始…

算法刷题打卡第34天:有效的井字游戏

有效的井字游戏 难度&#xff1a;中等 给你一个字符串数组 boardboardboard 表示井字游戏的棋盘。当且仅当在井字游戏过程中&#xff0c;棋盘有可能达到 boardboardboard 所显示的状态时&#xff0c;才返回 truetruetrue 。 井字游戏的棋盘是一个 3 x 3 数组&#xff0c;由字…

MYSQL 中连接的使用

文章目录0 写在前面1 语法说明2 SQL准备3 举例说明3.1 内连接3.2 左连接3.3 右连接4 写在最后0 写在前面 实际业务中&#xff0c;查询数据库大多都是多表链接查询&#xff0c;所以MYSQL的连接的使用非常重要。 连接有三种: INNER JOIN&#xff08;内连接,或等值连接&#xff0…

[附源码]计算机毕业设计疫情背景下社区互助服务系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Git基本使用

目录 一、Git基础 1.Git与Github 2.注册Github账号并安装Git 二、Git的使用 1.本地仓库 进行全局配置&#xff1a; 创建仓库&#xff1a; Git常用操作指令&#xff1a; 版本回退&#xff1a; 2.远程仓库 两种常规使用方式&#xff1a; 分支管理&#xff1a; 冲突的产生…

python http模块快速搭建“简单”服务器笔记

极简运行 python -m http.sever或 # test.py import http.server import socketserverPORT 8000Handler http.server.SimpleHTTPRequestHandlerwith socketserver.TCPServer(("", PORT), Handler) as httpd:print("serving at port", PORT)httpd.serve…

m基于matlab的光通信的信道估计,均衡,抑制papr误码率仿真,对比ZF,RLS,MMSE三种算法

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 可见光通信的信道估计&#xff0c;均衡&#xff0c;抑制papr。 不考虑光信道&#xff0c;用传统的无线通信的OFDM的信道估计&#xff0c;均衡&#xff0c;抑制papr 信道估计&#xff0c;均衡最…

解决Windows RDP远程桌面连接同时多用户连接不互踢不干扰问题及rdp wrapper windows版本不受支持问题.

注意: 如果你当前正在通过RDP方式远程连接到你要更改设置的目标计算机,请在目标计算机上安装其他远程控制软件.防止RDP因在此过程中挂掉无法连接. 下载: 使用GitHub - stascorp/rdpwrap: RDP Wrapper Library开源软件 转到Release 最新版本的是1.6.2,原作者没有更新的了.好…

MySQL中count(*)和information_schema.tables中的table_rows值不相同

前两天我还在高高兴兴地写了一篇文章《一条SQL查询出MySQL数据库中所有表的数据量大小》&#xff0c;心想这也太方便了&#xff0c;只用一条SQL就能统计出所有表的数据量&#xff0c;但没想到&#xff0c;最终还是翻车了。。。 翻车过程如下&#xff1a; 有一张表&#xff0c…

【多目标进化优化】 MOEA 测试函数

声明 本文内容来源于 《多目标进化优化》 郑金华 邹娟著&#xff0c;非常感谢两位老师的知识分享&#xff0c;如有侵权&#xff0c;本人立即删除&#xff0c;同时在此表示&#xff0c;本文内容仅学习使用&#xff0c;禁止侵权&#xff0c;谢谢&#xff01; 注&#xff1a;本文…

HTML+CSS制作简单的家乡网页 ——我的家乡介绍广东 web前端期末大作业

家乡旅游景点网页作业制作 网页代码运用了DIV盒子的使用方法&#xff0c;如盒子的嵌套、浮动、margin、border、background等属性的使用&#xff0c;外部大盒子设定居中&#xff0c;内部左中右布局&#xff0c;下方横向浮动排列&#xff0c;大学学习的前端知识点和布局方式都有…

基于STM32单片机的温度报警器(数码管)(Proteus仿真+程序)

编号&#xff1a;26 基于STM32单片机的温度报警器&#xff08;数码管) 功能描述&#xff1a; 本设计由STM32F103单片机最小系统DS18B20温度传感器数码管显示模块声光报警模块独立按键组成。 1、主控制器是STM32F103单片机 2、DS18B20温度传感器测量温度 3、数码管显示温度值&…

数字化改革“1612”详解

背景 2021年2月18日&#xff0c;浙江省开始数字化改革建设&#xff0c;通过一年的建设&#xff0c;取得了光辉成绩。2022年2月28日&#xff0c;在省数字化改革推进大会&#xff0c;回顾一年来数字化改革的主要成效&#xff0c;研究部署2022年数字化改革目标任务&#xff0c;在…