优秀数据结构2--莫队
众所周知,欣隆妹妹是个毒瘤。
经常出毒瘤数据结构题。
出得挺多的是莫队。
所以今天我们来聊聊莫队
可爱的由乃。
part one - 什么是莫队?
。。。
part two - 莫队的原理与实现:
第一步,让询问离线。
你将每一个询问放进一个结构体,然后对询问进行分块,每一个询问对应的块记为bel,目前,块大小最优为n/sqrt(m*2/3)
如何排序让时间复杂度正确???主流的做法是,第一关键字为左端点所在的块,如果同块则比右端点
你还可以尝试如果左端点所在块为奇数则a.r>b.r否则a.r<b.r,这样可能比较块,但也挺玄学
莫队需要两个指针,每次移动两个指针,让他对应到当前的l,r,每一次删除或加入时更新答案
part three 莫队基础题:
1.小z的袜子:
求区间内取到相同颜色袜子的概率(差不多就是方案数
设每一种颜色在区间中出现的次数为c[i],那么对答案的贡献就是c[i]*(c[i]-1)/2
然后每一次删除,插入,更改c[i],重新算一遍贡献,然后减去对但减去原来的贡献加上新的贡献
code::(贡献一波手码的排序)
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #define Rint register int #define Temp template<typename T> Temp inline void read(T &x) { x=0;T w=1,ch=getchar(); while(!isdigit(ch)&&ch!='-') ch=getchar(); if(ch=='-') w=-1,ch=getchar(); while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); x*=w; } const int maxn=5e5+10; int a[maxn],t[maxn],f[maxn],res[maxn],x[maxn],y[maxn],bh[maxn]; int n,m,left=1,right=1,p; long long ans=0; inline int gcd(int n,int m) { if(n%m==0) return m; return gcd(m,n%m); } inline void write(long long x) { if(x>=10) write(x/10); putchar(x%10+'0'); } inline void qsort(int l,int r) { int i=l,j=r,mid=(l+r)>>1,tl=x[(l+r)>>1],tr=y[(l+r)>>1]; do{ if((i/p)==(j/p)) { while(x[i]<tl) ++i; while(tl<x[j]) --j; if(i<=j) { std::swap(x[i],x[j]); std::swap(y[i],y[j]); std::swap(bh[i],bh[j]); ++i; --j; } } else { while(y[i]<tr) ++i; while(tr<y[j]) --j; if(i<=j) { std::swap(x[i],x[j]); std::swap(y[i],y[j]); std::swap(bh[i],bh[j]); ++i; --j; } } }while(i<=j); if(l<j) qsort(l,j); if(i<r) qsort(i,r); } int main() { read(n);read(m); p=sqrt(n); for (Rint i=1;i<=n;++i) { read(a[i]); } for (Rint i=1;i<=m;++i) { read(x[i]);read(y[i]);bh[i]=i; } qsort(1,m); t[a[1]]++; for (Rint i=1;i<=m;++i) { int ll=x[i]; int rr=y[i]; while(left>ll) { --left; ans+=t[a[left]]; t[a[left]]++; } while(right<rr) { ++right; ans+=t[a[right]]; t[a[right]]++; } while(left<ll) { t[a[left]]--; ans-=t[a[left]]; ++left; } while(right>rr) { t[a[right]]--; ans-=t[a[right]]; --right; } f[bh[i]]=ans; res[bh[i]]=right-left+1; } for (Rint i=1;i<=m;++i) { if(f[i]==0) { putchar('0');putchar('/');putchar('1');putchar('\n'); } else { long long tot=res[i]; long long c=gcd(f[i],tot*(tot-1)>>1); write(f[i]/c);putchar('/');write(tot*(tot-1)/c/2);putchar('\n'); } } return 0; }
2.HH的项链
莫队,统计出现次数。
每一次插入,当前a[i]的出现次数+1,如果a[i]的出现次数为1,答案+1
每一次删除,当前a[i]的出现次数-1,如果a[i]的出现次数为0,答案-1
题太菜就没code了
3.采花
莫队,还是统计出现次数
每一次插入,当前a[i]的出现次数+1,如果a[i]的出现次数为2,答案+1
每一次删除,当前a[i]的出现次数-1,如果a[i]的出现次数为1,答案-1
code::(为啥要给的来着
#include <iostream> #include <stdio.h> #include <string.h> #include <math.h> #include <algorithm> using namespace std; struct query{ int l,r,id; }q[300005]; int num[300005],Ans[300005],n,m,a[300005],bel[300005],sz,ans=0,c; int read() { register int x=0,w=1;char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); return x*w; } bool cmp(query a,query b) { if(bel[a.l]!=bel[b.l]) return a.l<b.l; else if(bel[a.l]&1) return a.r>b.r; else return a.r<b.r; } void ins(int x) { ++num[a[x]]; if(num[a[x]]==2) ++ans; } void del(int x) { --num[a[x]]; if(num[a[x]]==1) --ans; } int main() { n=read();c=read();m=read(); sz=m/sqrt(n*2/3); for (int i=1;i<=n;++i) a[i]=read(); for (int i=1;i<=n;++i) bel[i]=(i-1)/sz+1; for (int i=1;i<=m;++i){ q[i].l=read();q[i].r=read();q[i].id=i; } sort(q+1,q+1+m,cmp); int l=1,r=0; for (int i=1;i<=m;++i) { while(l<q[i].l) del(l++); while(l>q[i].l) ins(--l); while(r<q[i].r) ins(++r); while(r>q[i].r) del(r--); Ans[q[i].id]=ans; } for (int i=1;i<=m;++i) printf("%d\n",Ans[i]); return 0; }
part five:Ynoi
1.这是我自己的发明
换根,就是分类讨论以下,其实就是dfs序上的两个区间
所以就成了不换根
如果颜色出现次数>sqrt(n)暴力
否则,就是类似二维数点。
因为修改O(m)个,查询O(nsqrt(n))个,考虑用分块代替树状数组,这样可以根号平衡
2.跳进兔子洞
莫队+bitset
3.此时此刻的光辉
因为质数个数少,所以根号分治
前<sqrt(n)的质数暴力,后面的莫队维护
所以时间复杂度是对的
end.
计划补充的内容:
1.回滚莫队
2.二次离线莫队
orz nzhtl1477
原文地址:https://www.cnblogs.com/zjjandrew/p/11313970.html
- 【强烈推荐】Java工程师如何从一名普通的码农成长为一位大神
- Remoting: Server encountered an internal error
- 一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](四)
- 在mono 3.0 下运行ASP.NET 4网站的主意事项
- 《干货系列》SQL语句-知无不言言无不尽
- OutOfMemoryError异常系列之方法区溢出和运行时常量溢出池溢出
- 代码转换工具 Code Converter 2013
- OutOfMemoryError异常系列之Java堆溢出
- android ndk之hello world
- ScheduledExecutorService和timer的异同
- 【精心解读】关于Jupyter Notebook的28个技巧
- Web项目接口自动化测试框架搭建
- 一文读懂Hadoop、HBase、Hive、Spark分布式系统架构
- 《Spring敲门砖之基础教程第一季》 第二章(1) Spring框架之IOC首例-HelloWorld
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 快速入门 Arrow 日期处理库
- 基于微前端的大型中台项目融合方案
- 基于【观察者设计模式】设计异步多渠道群发框架
- 开胃菜解析
- SpringBootAdmin2.0实现微服务应用监控
- 如何防止重复发送ajax请求
- 说说 C# 8 using 新特性
- SQL Server 每日一题--老二解析
- SQL Server 每日一题--重复报名的人解析
- 逻辑回归项目实战-附Python实现代码
- 详解C# 序列化和反序列化
- SQL Server 每日一题--解析只逛不买
- 【Python】基于某些列删除数据框中的重复值
- SQL Server 每日一题--解析天气预报员
- 未读消息(小红点),前端与 RabbitMQ实时消息推送实践,贼简单~