带修改的莫队算法学习小记

简介

莫涛大神创造出的离线询问算法的带修改版。

算法基础:需要掌握莫队算法,会打暴搜(暴力)。

一个叫的双端队列。

只支持单点修改

操作方法

普通的不带修改的莫队算法要把每个询问带上两个关键字排序,现在待修改的莫队算法要带上三个关键字排序。

初始操作

fo(i,1,m) {
        scanf("%s%d%d",s,&k,&l);
        if (s[0]==‘Q‘)a[++tot].l=k,a[tot].r=l,a[tot].x=num,a[tot].p=tot;
        else d[++num].x=k,d[num].o=gai[k],d[num].y=l,gai[k]=l;
    }

代码中‘Q’是询问,否则是修改。

a是询问数组

a[tot]{

l:询问的左边——第一个关键字

r:询问的右边——第二个关键字

x:此次询问的上一个修改操作——第三个关键字

p:此次询问的序号——用于统计答案

}

d是修改数组

d[num]{

x:此次修改哪一个节点的值

y:修改为什么值

o:此次修改时原来的值——用于还原数组

}

排序

da=1300;
    fo(i,1,n)scanf("%d",&b[i]),gai[i]=b[i],kuai[i]=(i-1)/da+1;

先定义每个块的大小,有时块大速度快,有时块小速度快。还要存每个节点在哪一个块。

bool cmp(node x,node y){
    return kuai[x.l]<kuai[y.l]||kuai[x.l]==kuai[y.l]&&kuai[x.r]<kuai[y.r]||kuai[x.l]==kuai[y.l]&&kuai[x.r]==kuai[y.r]&&x.p<y.p;
}

排序条件

sort(a+1,a+tot+1,cmp);

对询问数组排序。

主程序——维护答案

l和r分别是上一次的操作区间左右端点在什么位置,now是上一个询问的上一个修改操作在d数组中的位置(及a[i-1].x)。

为了方便操作,初始值:l=1,r=0,now=0。

    l=1;
    fo(i,1,tot){
        if (now<a[i].x)fo(j,now+1,a[i].x)change(d[j].x,d[j].y);
        else fod(j,now,a[i].x+1)change(d[j].x,d[j].o);
        if (l<a[i].l)fo(j,l,a[i].l-1)update(j);
        else fo(j,a[i].l,l-1)update(j);
        if (r<a[i].r)fo(j,r+1,a[i].r)update(j);
        else fo(j,a[i].r+1,r)update(j);
        ans1[a[i].p]=ans;
        l=a[i].l;r=a[i].r;now=a[i].x;
    }

对于当前的询问操作a[i],只有序号为a[i].x及其以前的修改操作才会影响到a[i]的询问。

如果now

更新、修改值

bz数组是标记当前x节点是否进队(相当于上一次询问操作是否包含x节点)

void change(int x,int y){
    if(bz[x]){
        update(x);
        b[x]=y;
        update(x);
    }
    else b[x]=y;
}

如果要修改当前x的值y为的话,要分一分情况:1、如果bz[x]=1,那么就是在上一次询问操作中含有x节点,所以修改了x节点可能会影响答案,那么就update(x)一次让x出队,再修改x一次让x入队;2、如果x没有被上一次询问包含,那么修改了也不会影响答案,所以直接修改就好了。

update不明白的下面马上说。(联系上下文是个好方法~~)

void update(int x){
    if(bz[x]){
        shu[b[x]]--;
        if(!shu[b[x]])ans--;
    }
    else{
        shu[b[x]]++;
        if(shu[b[x]]==1)ans++;
    }
    bz[x]^=1;
}

有些打法直接在update加个1和-1表示加入和删除就好了,但是这样打有点长。

但是用bz[x]表示x在上一次询问是否涉及到,那么要对已经入队的x进行update(x)操作就是要让它出队并更新答案;如果x在上一次询问中没有涉及到就是bz[x]=0,那么对x进行update(x)操作就相当于让x入队并对答案更新。

注意bz[x]进行update操作后要取反。

输出答案

fo(i,1,tot)printf("%d\n",ans1[i]);

由于本人是个蒟蒻

对于莫队的修改的了解也就这么多。

时间: 2024-08-07 00:00:25

带修改的莫队算法学习小记的相关文章

bzoj 2120: 数颜色(带修改的莫队算法)

2120: 数颜色 Time Limit: 6 Sec  Memory Limit: 259 MB Submit: 2908  Solved: 1130 [Submit][Status][Discuss] Description 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会像你发布如下指令: 1. Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔. 2. R P Col 把第P支画笔替换为颜色Col.为了满足墨墨的要求,你知道你需

BZOJ 2120: 数颜色 带修改的莫队算法 树状数组套主席树

https://www.lydsy.com/JudgeOnline/problem.php?id=2120 标题里是两种不同的解法. 带修改的莫队和普通莫队比多了个修改操作,影响不大,但是注意一下细节不要出现zz错误. 这道题修改的数量比较少可以写莫队,但是如果修改数量多或者是特别极限的数据大概是不行的吧. 1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstrin

莫队算法学习小记

算法创始人 莫涛大神. 莫涛队长的算法,%%%%%%%%% 算法简介 算法前提 可以在O(1)的时间内把[l,r]的询问转移到[l-1,r],[l+1,r],[l,r-1],[l,r+1]的询问,而且不需要修改操作,那么就可以使用莫队算法([a,b]表示从a到b的区间,包含a和b) 算法核心 假如有一个询问[l,r]要转移到一个询问[l1,r1],那么需要的时间为O(|l1?l|+|r1?r|),在算法前提下,可以用这么多的时间暴力转移. 但是可以发现有时候有些点会被来回算很多次,这样大量浪费了

BZOJ 2120 数颜色&amp;2453 维护队列 [带修改的莫队算法]【学习笔记】

题意: 询问区间中不同颜色的个数,单点修改颜色 发现以前写的学习笔记没法看,于是重写一下(不就是会用latex了嘛) 额外维护一个当前修改操作执行到的时间 如果要进行某个查询操作,修改操作的时间必须移动到这个查询操作处 按照$(pos[l], pos[r], tim)$排序 令$S=N^{\frac{2}{3}}$, 有$N^{\frac{1}{3}}$块 $l$移动$N*N^{\frac{2}{3}}$次 $r$移动$N*N^{\frac{1}{3}}+N*N^{\frac{2}{3}}$次

带修改的莫队

在学习了最基础的莫队后,我们会发现普通莫队并不资瓷修改操作啊!!!这就很尴尬,那么莫队就没办法修改吗,反正当时发明莫队的人是没有提到的,但是不要小瞧了智慧的OI人,不久就有人就提出了带修改莫队的想法. 如果你还没不知道莫队 点击这里!!! 其实刚知道莫队还可以资瓷修改时,我以为代码量会有质的飞越,但是学习后才发现原来不过如此,所以就别担心了... 首先还是来一道题 数颜色 题目描述 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会向你发布如下指令: 1.

莫队算法学习笔记【BZOJ2038:小Z的袜子】【SPOJ3267:D-query】

很久以前傻乎乎地看来源奇怪的资料的时候被各种曼哈顿弄晕了. 然后现在学会的是分块方法.另新创一个分块方法. 让我们考虑这样一个区间询问问题…… 它有如下的性质: 0,n个数,Q个询问. 1,它没有修改操作,这意味着我们可以按我们喜欢的次序跟询问玩耍.实际上后面会讲到我们完全可以按任意次序玩耍. 2,如果我们知道区间询问 [L , R] 对应的值,我们可以轻易求出 [L±1 , R] 和 [L , R±1] 的值. (其实如果限制增加,比如只能求 [L+1 , R] 和 [L , R-1] 的值,

BZOJ 4129 Haruna’s Breakfast 带修改树上莫队+分块

题目大意:给定一棵树,每个点有一个非负点权,支持下列操作 1.修改某个点的点权 2.查询某条链上的mex 考虑链上不带修改的版本,我们可以用莫队+分块来搞(链接戳这里) 现在到了树上带修改,果断糖果公园 本来抱着逗比的心态写了一发结果1.4s过了 跟糖果公园的80s完全不成正比啊0.0 #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <

BZOJ 3052 WC2013 糖果公园 带修改树上莫队

题目大意:给定一棵树,每个点有一个颜色,提供两种操作: 1.询问两点间路径上的Σv[a[i]]*w[k],其中a[i]代表这个点的颜色,k表示这个点是这种颜色第k次出现 2.修改某个点的颜色 VfleaKing的题解见 http://vfleaking.blog.163.com/blog/static/174807634201311011201627/ 带修改莫队上树--如果不带修改就正常搞就好了 如果带修改的话,令块的大小为n^(2/3) 将询问排序时第一键值为左端点所在块,第二键值为右端点所

[bzoj2038]莫队算法学习

解题关键:莫队最重要的是区间之间以$O(1)$的复杂度进行转化,由于电脑原因,后续补上公式推导. 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<iostream> 6 #include<cmath> 7 using namespace std; 8 typedef long long ll; 9 l