首页 > 其他分享 >莫队

莫队

时间:2024-02-16 09:00:31浏览次数:28  
标签:cnt return int ++ time now 莫队

莫队

介绍

莫队算法可以解决一类离线区间询问问题,适用性极为广泛。同时将其加以扩展,便能轻松处理树上路径询问以及支持修改操作。

普通莫队算法

形式

假设 \(n=m\),那么对于序列上的区间询问问题,如果从 \(\left[l,r\right]\) 的答案能够 \(O(1)\) 扩展到 \(\left[l,r+1\right],\left[l+1,r\right],\left[l,r-1\right],\left[l-1,r\right]\) 即与 \(\left[l,r\right]\) 相邻的区间)的答案,那么可以在 \(O(n\sqrt n)\) 的复杂度内求出所有询问的答案。

例题

P1494

实现

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e5+5;
int ans[N],cnt[N],t,c[N];
int fans[N][3],ans1 = 0,ans2 = 0;
struct node{
	int l,r,id;
}a[N];
int gcd(int x,int y){
	return (y == 0)?x:gcd(y,x%y);
}
bool cmp(node x,node y){
	if(x.l/t == y.l/t){
		return x.r < y.r;
	}else{
		return x.l < y.l;
	}
}
void add(int x){
	ans2 = ((int)sqrt(ans2)+1ll) * ((int)sqrt(ans2)+2ll);
	ans1 -= cnt[c[x]] * (cnt[c[x]]-1ll);
	cnt[c[x]] ++;
	ans1 += cnt[c[x]] * (cnt[c[x]]-1ll);
	return;
}
void del(int x){
	ans2 = ((int)sqrt(ans2)) * ((int)sqrt(ans2)-1ll);
	ans1 -= cnt[c[x]] * (cnt[c[x]]-1ll);
	cnt[c[x]] --;
	ans1 += cnt[c[x]] * (cnt[c[x]]-1ll);
	return;
}
signed main(){
// 	freopen("P1494_2.in","r",stdin);
	int n,m;
	scanf("%lld%lld",&n,&m);
	t = sqrt(n);
	for(int i=1;i<=n;i++){
		scanf("%lld",&c[i]);
	}
	for(int i=1;i<=m;i++){
		scanf("%lld%lld",&a[i].l,&a[i].r);
		a[i].id = i;
//		cout<<i;
	}
	sort(a+1,a+1+m,cmp);
	int l = 1,r = 0;
	for(int i=1;i<=m;i++){
		while(r < a[i].r){
			add(++r);
		}
		while(l > a[i].l){
			add(--l);
		}
		while(r > a[i].r){
			del(r--);
		}
		while(l < a[i].l){
			del(l++);
		}
		fans[a[i].id][1] = ans1;
		fans[a[i].id][2] = ((int)sqrt(ans2)) * ((int)sqrt(ans2)-1);;
	}
	for(int i=1;i<=m;i++){
		int g = __gcd(fans[i][1],fans[i][2]);
		if(g == 0){
			printf("0/1\n");
			continue;
		}
		printf("%lld/%lld\n",fans[i][1]/g,fans[i][2]/g);	
	}
	return 0;
} 

优化

奇偶化排序优化,无 \(O2\),实测运行时间为 1.00s->727ms

bool cmp(node x,node y){
	if(x.l/t != y.l/t){
		return x.l < y.l;
	}else if(x.l/t & 1){
		return x.r < y.r;
	}else{
		return x.r > y.r;
	}
}

带修改莫队

询问由 \([l,r]\) 变为 \([l,r,time]\),所以多了一个扩展维度:

  • \([l+1,r,time]\);
  • \([l-1,r,time]\);
  • \([l,r+1,time]\);
  • \([l,r-1,time]\);
  • \([l,r,time+1]\);
  • \([l,r,time-1]\)。

例题

P1903

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+5;
int c[N],t,fans[N];
int c_cnt[N],ans = 0;
struct node{
	int l,r,time,id;
}a[N];
struct change_node{
	int x,c;
}b[N];
bool cmp(node x,node y){
	if(x.l/t == y.l/t){
		if(x.r/t == y.r/t){
			return x.time < y.time;
		}else{
			return x.r < y.r; 
		}
	}else{
		return x.l < y.l;
	}
}
void add(int x){
	if(c_cnt[c[x]] == 0){
		ans ++;
	}
	c_cnt[c[x]] ++;
}
void del(int x){
	c_cnt[c[x]] --;
	if(c_cnt[c[x]] == 0){
		ans --;
	}
}
int main(){
	int n,m,cntq = 0,cntr = 0;
	scanf("%d%d",&n,&m);
	t = pow(n,2.0/3.0);
	for(int i=1;i<=n;i++){
		scanf("%d",&c[i]);
	}
	for(int i=1;i<=m;i++){
		char opt;
		std::cin>>opt;
		if(opt == 'Q'){
			cntq ++;
			scanf("%d%d",&a[cntq].l,&a[cntq].r);
			a[cntq].id = cntq;
			a[cntq].time = cntr;
		}else if(opt == 'R'){
			cntr ++;
			scanf("%d%d",&b[cntr].x,&b[cntr].c);
		}
	}
	sort(a+1,a+1+cntq,cmp);
	int l = 1,r = 0,now = 0;
	for(int i=1;i<=cntq;i++){
		while(l > a[i].l){
			add(--l);
		}
		while(r < a[i].r){
			add(++r);
		}
		while(l < a[i].l){
			del(l++); 
		}
		while(r > a[i].r){
			del(r--);
		}
		while(now < a[i].time){
			now ++;
			if(l <= b[now].x && r >= b[now].x){
				del(b[now].x);
				swap(c[b[now].x],b[now].c);
				add(b[now].x);
			}else{
				swap(c[b[now].x],b[now].c);
			}
		}
		while(now > a[i].time){
			if(l <= b[now].x && r >= b[now].x){
				del(b[now].x);
				swap(c[b[now].x],b[now].c);
				add(b[now].x);
			}else{
				swap(c[b[now].x],b[now].c);
			}
			now --;
		}
		fans[a[i].id] = ans;
	}
	for(int i=1;i<=cntq;i++){
		printf("%d\n",fans[i]);
	}
}

标签:cnt,return,int,++,time,now,莫队
From: https://www.cnblogs.com/WhileTrueRP/p/18016900/mo_dui

相关文章

  • 莫队学习笔记
    莫队莫队是一种常见的离线处理区间查询问的方法。莫队的思想是把序列分块,然后把询问按照左端点所在块为第一关键字,右端点为第二关键字排序,然后处理询问,维护指针\(l,r\)表示当前处理的区间是\([l,r]\),每次根据询问区间来移动指针计算贡献。关于复杂度。假设指针移动的复杂度......
  • 莫队
    莫队莫队的运用非常广泛,今天我们就主要收录莫队的常见运用,以及一些思维和实践上的小技巧。基础莫队众所周知莫队是一种离线算法,而这里的基础莫队,指的是多关键字的莫队。基础莫队要求支持计算单个元素变化的贡献,并且与元素贡献的顺序无关。当我们的询问有多个关键字时,我们可以......
  • 分块与莫队算法
    1.分块分块的思想分块是把一个长度为\(N\)的数列分为\(\sqrt{N}\)个块,每个块的长度为\(\sqrt{N}\)。这样在区间操作的时候,对于每个涉及到的块,如果涉及到半个块,就直接操作;如果涉及到整个块,就整体操作。下面通过例题来了解一下分块。例1洛谷-P3372这道题可以用分块来做......
  • 莫队算法
    简要介绍:莫队算法是先进行分块,然后按照分块来排序,然后对已知区间进行增减以达到所求区间,记录下答案,最后按照所询问的顺序进行输出答案。如对于已知区间[l,r]要求[l-1,r],[l-2,r],[l-3,r],[l-4,r],则将已知区间向左移,同时进行add添加操作;而对于要求[l,r+1],[l,r+2],[l,r+3],[l,r+......
  • 莫队
    Part-1前言本文为莫队学习笔记,如果有错误,请提出,谢谢捏。Part0目录普通莫队1.形式2.算法流程3.小trik3.例题Part1普通莫队1.形式假如说通过区间\([l,r]\)的答案可以\(O(1)\)求出\([l-1,r]\),\([l+1,r]\),\([l,r-1]\)和\([l,r+1]\)的答案那么我......
  • 莫队算法/分块思想
    莫队算法/分块思想引入对于区间问题,常常会使用线段树维护,但是对于一些数据合并复杂度无法\(O(1)\)解决。所以不能使用,应当使用莫队算法。定义对于离线处理的查询问题,通过合理安排这些计算的次序,得到一个较优的复杂度例题1一个长度为\(n\)的序列,询问\(m\)次\([L,R]\)......
  • 浅谈莫队
    莫队基础莫队[SDOI2009]HH的项链这道题是卡莫队的,但是确实练习莫队的好题。首先想一下暴力:直接暴力枚举询问,然后再枚举区间,这样是O(n^2)的;想一下优化:如果说询问是按照左端点递增&&右端点递增的;那么我们就可以离线排序,用线性的时间扫过去所有询问,用桶记录一下就行,同......
  • 莫队学习笔记
    前置知识:分块莫队是非常好的数据结构,可以离线解决很多序列问题当对于一个查询\([l,r]\)可以\(O(1)\)转移到\([l-1,r],[l+1,r],[l,r-1],[l,r+1]\)时可以考虑用(普通)莫队莫队先读入所有的询问,接着离线对于所有询问区间\([l,r]\),用\(l\)所在块的编号为第一关键字,\(r\)为......
  • 莫队
    普通莫队由莫涛总结并实现。可以在\(\mathcal{O(n\sqrt{n})}\)的时间复杂度内解决不带修的区间问题。那什么样的题才能用莫队呢?最重要的特征是知道区间\([l,r]\)的答案,可以\(\mathcal{O(1)}\)得知\([l-1,r]\),\([l,r-1]\),\([l+1,r]\),\([l,r+1]\)区间的答案。先来看一个......
  • 树套树板子,但是带修莫队+值域分块
    \(\text{Link-LuoguBlog}\)原题传送门没啥重要的事情,就是终于过了这题非常开心,发现自己是莫队的时间戳部分写错了调了114514年我也只能说是十分趣味。以及今天深刻地认识到了带修莫队应该len=pow(n,0.66);。就是裸的带修莫队+值域分块,就不说了,直接放代码了昂。如果有人......