[莫队]
莫队
特点
是一种优雅的暴力
解决大部分区间离线问题的离线算法
主要思想为分块,将 \(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;
}