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

26 篇文章 0 订阅
25 篇文章 8 订阅

简介

莫涛大神创造出的离线询问算法的带修改版。
算法基础:需要掌握莫队算法,会打暴搜(暴力)。
一个叫的双端队列。
只支持单点修改

操作方法

普通的不带修改的莫队算法要把每个询问带上两个关键字排序,现在待修改的莫队算法要带上三个关键字排序。
这也是主要思想,和普通的莫队一样很简单的思想。
原本的莫队是[l,r]向连边推,现在带修改那么就设设三元(l,r,x),x为已经操作了x次修改,可以向(l±1,r,x),(l,r±1,x),(l,r,x±1)推,原理一样。

初始操作
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]&&x.r<y.r||kuai[x.l]==kuai[y.l]&&x.r==y.r&&x.x<y.x;
}

排序条件

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< a[i].x说明d[now+1到a[i].x]这些修改操作还没有进行,否则如果now>a[i].x说明d[a[i].x+1到now]这些修改操作多进行了——但是为什么是倒着做呢,因为后面的操作进行之后,不知道原来的值,要倒着逐个修改为d[j].o(此次修改上一个的值)才能还原成原来的值。
接下来就是普通莫队算法的正常操作。
但是正常的加入删除的update不是要打1或-1吗?
这里用了一个小技巧。

更新、修改值

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]);

题目

uva12345 Dynamic len

由于本人是个蒟蒻

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

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
在这里最重要的莫过于select模型和Asynchronous I/O模型。从理论上说,AIO似乎是最高效的,你的IO操作可以立即返回,然后等待os告诉你IO操作完成。但是一直以来,如何实现就没有一个完美的方案。最著名的windows完成端口实现的AIO,实际上也只是内部用线程池实现的罢了,最后的结果是IO有个线程池,你的应用程序也需要一个线程池...... 很多文档其实已经指出了这引发的线程context-switch所来的代价。在linux 平台上,关于网络AIO一直是改动最多的地方,2.4的年代就有很多AIO内核patch,最著名的应该算是SGI。但是一直到2.6内核发布,网络模块的AIO一直没有进入稳定内核版本(大部分都是使用用户线程模拟方法,在使用了NPTL的linux上面其实和windows的完成端口基本上差不多了)。2.6内核所支持的AIO特指磁盘的AIO---支持io_submit(),io_getevents()以及对Direct IO的支持(即:就是绕过VFS系统buffer直接写硬盘,对于流服务器在内存平稳性上有相当的帮助)。 所以,剩下的select模型基本上就成为我们在linux上面的唯一选择,其实,如果加上no-block socket的配置,可以完成一个"伪"AIO的实现,只不过推动力在于你而不是os而已。不过传统的select/poll函数有着一些无法忍受的缺点,所以改进一直是2.4-2.5开发版本内核的任务,包括/dev/poll,realtime signal等等。 最终,Davide Libenzi开发的epoll进入2.6内核成为正式的解决方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值