首页 > 其他分享 >『学习笔记』莫队

『学习笔记』莫队

时间:2024-02-17 22:23:45浏览次数:32  
标签:cnt int sum ++ 笔记 学习 -- add 莫队

Part 0. 目录

  • 概念
  • 普通莫队
  • 树上莫队
  • 带修莫队

Part 1. 概念

莫队是由莫涛提出的算法。莫队算法可以解决一类离线区间询问问题,适用性极为广泛。

Part 2. 普通莫队

普通莫队主要针对于多次区间询问的问题,基于分块的思想。

过程如下:

先将当前区间 \([l,r]\) 设为 \([1,0]\),再每次移动一个端点,即变为 \([l-1,r],[l,r+1],[l+1,r],[l,r-1]\)。也就是说每次移动长度会加 \(1\) 或减 \(1\),从而会多一个元素或减一个元素,进而我们就产生了两个用于在移动时记录答案的函数 \(\operatorname{add}\) 和 \(\operatorname{del}\)。

调用的代码如下:

inline void add(int x) {
  //...
}
inline void del(int x) {
  //...
}
sort(q + 1, q + m + 1);
int l = 1, r = 0;
for(int i = 1; i <= m; i++) {
  while(l > q[i].l) { add(a[--l]); }
  while(r < q[i].r) { add(a[++r]); }
  while(l < q[i].l) { del(a[l++]); }
  while(r > q[i].r) { del(a[r--]); }
  ans[q[i].id] = sum;
}

而排序怎么排以及块长 \(bl\) 取多少就决定了代码的时间复杂度。

一般的排序方式是先按左端点所在块为第一关键字,再按右端点为第二关键字排序,代码如下:

return b[l] != b[x.l] ? l < x.l : r < x.r;

但对于这种:

画框部分就多移动了两遍,于是我们考虑奇偶化排序,也就是若左端点所在块相等,则若为奇数块,则按右端点升序排序;否则按右端点降序排序。

代码如下:

return b[l] != b[x.l] ? l < x.l : (r == x.r ? 0 : (b[l] & 1) ^ (r < x.l));

此时排序情况如下,很明显减少了一倍的移动量。

时间复杂度为 \(\mathcal{O}\left(m(\sqrt{n}+\log_2m)\right)\)。

接着便是一堆例题。

SP3267 & P1972

题目链接:link1 and link2

题目大意:每次询问求区间 \([l,r]\) 内的权值种数。

题目的关键在于 \(\operatorname{add}\) 和 \(\operatorname{del}\) 两个函数。

对于一个数 \(a_i\),在它刚进入区间 \([l,r]\) 时,那么 \(a_i\) 出现的个数加 \(1\),若它之前未出现过,代表种数多 \(1\)。

同理,在它刚离开区间 \([l,r]\) 时,\(a_i\) 出现的个数减 \(1\),若现在其个数为 \(0\),代表种数少 \(1\)。

两个函数代码如下:

inline void add(int x) { sum += !cnt[x]++; }
inline void del(int x) { sum -= !--cnt[x]; }

PS:P1972 需要卡常,莫队并非正解,但卡的过去。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 5;
int n, m, c[N], bl, b[N], sum = 0, cnt[N], ans[N];
struct node {
  int l, r, id;
  bool operator < (const node &x) const {
    if(b[l] != b[x.l]) { return b[l] < b[x.l]; }
    return r < x.r;
  }
} a[N];
inline void add(int x) { sum += !cnt[x]++; }
inline void del(int x) { sum -= !--cnt[x]; }
signed main() {
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  cin >> n;
  bl = sqrt(n + 1);
  for(int i = 1; i <= n; i++) {
    cin >> c[i];
    b[i] = (i - 1) / bl + 1;
  }
  cin >> m;
  for(int i = 1; i <= m; i++) {
    cin >> a[i].l >> a[i].r;
    a[i].id = i;
  }
  sort(a + 1, a + m + 1);
  int l = 1, r = 0;
  for(int i = 1; i <= m; i++) {
    while(l > a[i].l) { add(c[--l]); }
    while(r < a[i].r) { add(c[++r]); }
    while(l < a[i].l) { del(c[l++]); }
    while(r > a[i].r) { del(c[r--]); }
    ans[a[i].id] = sum;
  }
  for(int i = 1; i <= m; i++) {
    cout << ans[i] << '\n';
  }
  return 0;
}

P1494

题目链接:link

题目大意:令区间 \([l,r]\) 内 \(x\) 出现了 \(c_x\) 次,求 \(\dfrac{\sum c_x\times(c_x-1)\div2}{(r-l+1)\times(r-l)\div2}\)。

同样是两个函数,但很好处理,每次 \(sum\) 加或减 \(c_x\) 即可。

P2709

题目链接:link

题目大意:求 \(\sum {c_x}^2\)。

对于每次移动,\(sum\) 加或减 \((c_x+1)^2-{c_x}^2=2c_x+1\)。

CF1000F

题目链接:link

题目大意:求任意一个 \(c_x=1\) 的 \(x\)。

用一个栈来存满足条件的 \(x\)。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int n, m, a[N], bl, sum = 0, cnt[N], ans[N];
int b[N], c[N], tot;
struct node {
  int l, r, b, id;
  bool operator < (const node &x) const {
    if(b != x.b) { return b < x.b; }
    return r == x.r ? 0 : (b & 1) ^ (r < x.r);
  }
} q[N];
inline void add(int x) {
  ++cnt[x];
  if(cnt[x] == 1) {
    ++sum;
    b[++tot] = x;
    c[x] = tot;
  }
  if(cnt[x] == 2) {
    --sum;
    b[c[x]] = b[tot];
    c[b[tot]] = c[x];
    b[tot--] = c[x] = 0;
  }
}
inline void del(int x) {
  --cnt[x];
  if(cnt[x] == 1) {
    ++sum;
    b[++tot] = x;
    c[x] = tot;
  }
  if(cnt[x] == 0) {
    --sum;
    b[c[x]] = b[tot];
    c[b[tot]] = c[x];
    b[tot--] = c[x] = 0;
  }
}
signed main() {
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  cin >> n;
  bl = sqrt(n);
  for(int i = 1; i <= n; i++) {
    cin >> a[i];
  }
  cin >> m;
  for(int i = 1; i <= m; i++) {
    cin >> q[i].l >> q[i].r;
    q[i].b = q[i].l / bl, q[i].id = i;
  }
  sort(q + 1, q + m + 1);
  int l = 1, r = 0;
  for(int i = 1; i <= m; i++) {
    while(l > q[i].l) { add(a[--l]); }
    while(r < q[i].r) { add(a[++r]); }
    while(l < q[i].l) { del(a[l++]); }
    while(r > q[i].r) { del(a[r--]); }
    if(sum) {
      ans[q[i].id] = b[tot];
    }
  }
  for(int i = 1; i <= m; i++) {
    cout << ans[i] << '\n';
  }
  return 0;
}

CF220B

题目链接:link

题目大意:有多少个 \(x\) 满足 \(c_x=x\)。

对于 \(\operatorname{add}\) 函数,如果 \(c_x\gets c_x+1\) 后 \(c_x=x\),满足条件,答案加 \(1\);如果 \(c_x=x+1\),代表这个 \(x\) 本来满足条件,现在不满足了,答案减 \(1\)。\(\operatorname{del}\) 函数同理。

两个函数代码如下:

inline void add(int x) {
  if(x > n) { return ; }
  ++cnt[x];
  if(cnt[x] == x) { ++sum; }
  if(cnt[x] == x + 1) { --sum; }
}
inline void del(int x) {
  if(x > n) { return ; }
  --cnt[x];
  if(cnt[x] == x) { ++sum; }
  if(cnt[x] == x - 1) { --sum; }
}

Part 3. 树上莫队

对于树上路径问题,通常是用欧拉序转换为线性,还要加上 LCA,所以代码通常会很长。对于子树的问题,通常用 dfn 序转换。

未完。

标签:cnt,int,sum,++,笔记,学习,--,add,莫队
From: https://www.cnblogs.com/cyf1208/p/18018537

相关文章

  • Python 机器学习 逻辑回归算法
    ​ 1、理解逻辑回归逻辑回归建立在线性回归之上。在线性回归中,模型预测的是一个连续的数值。而在逻辑回归中,线性回归的输出被输入到Sigmoid函数中,用于预测某个类别的概率。Sigmoid函数是一个S形的曲线,它将任意实数映射到(0,1)区间,适合用来表达概率。逻辑回归广泛应用于各种......
  • 【机器学习】机器学习常见算法详解第4篇:KNN算法计算过程(已分享,附代码)
    本系列文章md笔记(已分享)主要讨论机器学习算法相关知识。机器学习算法文章笔记以算法、案例为驱动的学习,伴随浅显易懂的数学知识,让大家掌握机器学习常见算法原理,应用Scikit-learn实现机器学习算法的应用,结合场景解决实际问题。包括K-近邻算法,线性回归,逻辑回归,决策树算法,集成学习,聚......
  • 2024 学习计划
    经常容易忘记东西,记录下来会好一点;时间安排:jdk8-source-code,预计Q1结束skywalking,预计Q2结束spring,预计Q3结束dynamoDB细节2024.2SkyWalking希望深入理解JavaAgent,字节码插桩、SPI、Arthas;希望搞清楚java编写的项目转成exe的方式;SkyWalking的官方......
  • C++——数据类型笔记
    在C++编程中,了解各类数据类型也是至关重要的。下面我会总结一下C++中的数据类型,包括基本类型,符合类型和自定义类型。方便自己整理和理解。1,基本类型C++中的基本类型是构建其他数据类型的基础,常见的基础类型包括整型,浮点型,字符型和布尔型:整型:用于表示整数,如int、short......
  • 【网课下载教程】网课视频下载攻略:让学习更高效
    在当今互联网时代,在线学习已成为越来越多人的选择。有时,我们希望离线观看网课视频,以避免网络不稳定等问题。本文将为您提供一篇详细的网课视频下载教程,助您更高效地学习。一、为什么下载网课视频?无需依赖网络:下载后的视频可以在没有网络的情况下观看,便于在交通工具、户外等环境......
  • 【Vue前端】vue使用笔记0基础到高手第2篇:Vue进阶知识点介绍(附代码,已分享)
    本系列文章md笔记(已分享)主要讨论vue相关知识。Vue.js是前端三大新框架:Angular.js、React.js、Vue.js之一,Vue.js目前的使用和关注程度在三大框架中稍微胜出,并且它的热度还在递增。Vue.js是一个轻巧、高性能、可组件化的MVVM库,同时拥有非常容易上手的API。Vue.js是一个构建数据驱动......
  • 机密(与学习无关)
    已看(排序与喜爱程度有一定关系):小林家的龙女仆(两季)紫罗兰永恒花园(正片+外传)游戏人生(正片+zero)在魔王城说晚安魔女之旅RE:creator为美好的世界献上祝福Fate(UBW)约会大作战(第一季)待看列表overlord刀剑神域命运石之门DarlingintheFrankxxP4118终结的炽天使推进列......
  • JAVA笔记(复习)(新)
    概述重要特点Java语言是面向对象的Java语言是健壮的。Java的强类型机制,异常处理,垃圾的自动收集等是Java程序健壮性的重要保证Java语言是跨平台性的。Java语言是解释型的JDK,JRE和JVM的包含关系JDK=JRE+开发工具集JRE=JVM+JavaSE标准类库(java核心类库)如果只想运行开发......
  • 机器学习中7种常用的线性降维技术总结
    上篇文章中我们主要总结了非线性的降维技术,本文我们来总结一下常见的线性降维技术。1、PrincipalComponentAnalysis(PCA)PrincipalComponentAnalysis(PCA)是一种常用的降维技术,用于将高维数据集转换为低维表示,同时保留数据集的主要特征。PCA的目标是通过找到数据中最大......
  • 【集训笔记】2024 寒假集训 第一天:最优化问题
    最优化问题二分许多最优化问题可以通过二分来转化为判定性问题。0-1分数规划0-1分数规划思想用于求解分式最优化问题。可以通过对分式二分判定,转化为某一式子大于/小于常数,然后求对应最值即可。动态规划动态规划算法的一大用处就是解决最优化问题。朴素的动态规划效率一般......