莫队
介绍
莫队算法可以解决一类离线区间询问问题,适用性极为广泛。同时将其加以扩展,便能轻松处理树上路径询问以及支持修改操作。
普通莫队算法
形式
假设 \(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