[莫队]

莫队

特点

是一种优雅的暴力

解决大部分区间离线问题的离线算法

主要思想为分块,将 \(n^2\) 降为 \(n \sqrt{n}\)

题目关键词包含\(n,m,k\),并有多个询问\(L_i,R_i\),求区间内的...

思想

相当于有两个指针\(L,R\),若当前询问的区间为\(l[i],r[i]\)那么会分别将 \(L,R\)\(l[i],r[i]\)的方向移动,并在移动时做出与题目要求相关的操作

模板

int sz = sqrt(n);//每一块的大小为根号n
for(int i=1;i<=n;i++)
  belong[i] = i / sz; //将每一个位置划分到对应的块里面去
sort();//先按照每次询问的左端点所在的块号排序,如果两个区间左端点所在的块相同,那么按照右端点小的在前
int L = 1,R = 0;//初始化两个指针,R = 0,是为了能将第一个添加进去
for(int i=1;i<=m;i++)
{
	while(q[i].l < L) add(--L);
	while(q[i].l > L) sub(L++);
	while(q[i].r < R) sub(R--);
	while(q[i].r > R) add(++R);
	ans[q[i].id] = res;//q[i].id表示原来它是第几次询问,因为要跟询问顺序保持一致,res为每次更新区间之后的答案
}

洛谷P2709 code

#include <cmath>
#include <cstdio>
#include <iostream>
#include <algorithm>
typedef long long LL;
using namespace std;
const int maxn = 5e4+5;
int n,m,k,a[maxn],belong[maxn],cnt[maxn];
LL res,ans[maxn];
struct range{
	int l,r,id;
}q[maxn];

bool cmp(range x,range y)
{
	return belong[x.l] == belong[y.l] ? x.r < y.r : belong[x.l] < belong[y.l];
}

void add(int x)
{
	res = res + 2 * cnt[a[x]] + 1;
	cnt[a[x]] ++;
	return ;
}

void sub(int x)
{
	res = res - 2 * cnt[a[x]] + 1;
	cnt[a[x]] --;
	return ;
}

int main()
{
	scanf("%d%d%d",&n,&m,&k);
	int sz = sqrt(n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		belong[i] = i / sz;
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&q[i].l,&q[i].r);
		q[i].id = i;
	}
	sort(q+1,q+m+1,cmp);
	int L = 1,R = 0;
	for(int i=1;i<=m;i++)
	{
		while(q[i].l < L) add(--L);
		while(q[i].l > L) sub(L++);
		while(q[i].r < R) sub(R--);
		while(q[i].r > R) add(++R);
		ans[q[i].id] = res;
	}
	for(int i=1;i<=m;i++)
		printf("%lld\n",ans[i]);
	return 0;
} 

带修改的莫队

区别

相比于普通莫队,多了在查询期间的修改操作,做法是开另一个数组记录是第几次修改了,如果当前的查询发生在第n次修改之后,那么就进行到第n次修改的状态,如果当前少于n次修改那么就前进直到修改到n次,如果当前修改次数多于n次,那么就后退直到后退为n次。

洛谷P1903 code

这个题十分毒瘤,卡常卡的给我T飞了,关键点是:1、原先块的大小为 \(\sqrt{n}\) 修改为\(n^{\frac{3}{4}}\),其他指数应该也行,但我只测了0.66和0.75发现0.75要快不少;2、修改cmp函数若左端点所在的块相同,找右端点所在块,若都相同则按照修改次数小的排序,3、快读;剩下的就改改\(add(),sub()\)还有新加的修改操作\(update()\)

#include <cmath>
#include <cctype>
#include <cstdio>
#include <iostream>
#include <algorithm>
typedef long long LL;
using namespace std;
const int maxn = 133336;
int n,m,k,a[maxn],belong[maxn],num,cntm,now_up;
int res,cnt[maxn*10],ans[maxn];
struct Q{
	int l,r,id,up_cnt;
}q[maxn];
struct R{
	int val,pos;
}up[maxn];

bool cmp(Q x,Q y)
{
	return (belong[x.l] ^ belong[y.l]) ? belong[x.l] < belong[y.l] : ((belong[x.r] ^ belong[y.r]) ? belong[x.r] < belong[y.r] : x.up_cnt < y.up_cnt);
}

void add(int x)
{
	if(!cnt[a[x]]) res ++;
	cnt[a[x]] ++;
	return ;
}

void sub(int x)
{
	cnt[a[x]] --;
	if(!cnt[a[x]]) res --;
	return ;
}

void update(int x,int L,int R)
{
	if(up[x].pos >= L && up[x].pos <= R)
	{
		cnt[a[up[x].pos]] --;
		if(!cnt[a[up[x].pos]]) res --;
		if(!cnt[up[x].val]) res ++;
		cnt[up[x].val] ++;
	}
	swap(a[up[x].pos],up[x].val);//给他“反悔”的机会,比如第二次修改时把当前位置的3改为9,那么如果要再倒回第二次修改之前,直接将两个交换,下次就是将9改为3了
	return ;
}

inline int read()
{
    int x = 0,f = 1;
    char c;
    while(!isdigit(c=getchar()))
    if(c == '-') f = -1;
    while(isdigit(c))
    x = (x<<3) + (x<<1) + (c&15),c = getchar();
    return x * f; 
}
inline void write(int x)
{
    if(x < 0)
    {
    	putchar('-');
		write(~x+1);
	}
    else {
        if(x > 9)
        write(x/10);
        putchar(x%10+'0');
    }
    return ;
}

int main()
{
	n = read(); m = read();
	int sz = pow(n,0.75);
	for(int i=1;i<=n;i++)
	{
		a[i] = read();
		belong[i] = (i-1) / sz;
	}
	for(int i=1;i<=m;i++)
	{
		char c;
		cin >> c;
		if(c == 'Q')
		{
			cntm ++;
			q[cntm].l = read(); q[cntm].r = read();
			q[cntm].id = cntm;
			q[cntm].up_cnt = num;
		}
		if(c == 'R')
		{
			num ++;
			up[num].pos = read(); up[num].val = read();
		}
	}
	sort(q+1,q+cntm+1,cmp);
	int L = 1,R = 0;
	for(int i=1;i<=cntm;i++)
	{
		while(q[i].l < L) add(--L);
		while(q[i].l > L) sub(L++);
		while(q[i].r < R) sub(R--);
		while(q[i].r > R) add(++R);
		while(now_up < q[i].up_cnt) update(++now_up,L,R);
		while(now_up > q[i].up_cnt) update(now_up--,L,R);
		ans[q[i].id] = res;
	}
	for(int i=1;i<=cntm;i++)
	{
		write(ans[i]);
		printf("\n");
	}
	return 0;
} 
posted @ 2024-02-29 15:58  风丨铃  阅读(2)  评论(0编辑  收藏  举报