【笔记篇】莫队算法(二)

嗯,我们已经能通过莫队处理查询线性结构上的查询了。
但是,这怎么能够呢= =
现在的题里面基本上都要牵扯上修改操作啊。。。
比如动不动就会出一个“把节点x的值修改为y的操作”。。。

而我们知道,莫队是离线算法,要把所有操作都读进来,那么修改操作就会对后面的查询操作造成影响。。
如何消除这种影响呢?
我们可以再加一个维度——时间。
我们对于每一个查询操作,记录一个时间T,表示之前进行过T次修改了。
然后排序的时候,按左端点的块编号为第一关键字,右端点的块编号为第二关键字,时间为第三关键字排序即可。

bool cmp(const query &a,const query &b){
    if(a.l/blk==b.l/blk){
        if(a.r/blk==b.r/blk) return a.ti<b.ti;
        return a.r<b.r;
    }
    return a.l<b.l;
}

对于每一个修改,我们只需要记录每个修改的修改前和修改后的信息,然后像移动L,R一样移动T即可。
定义一个修改的结构体,像这样:

struct modify{
    int pos,pre,now; //这次修改将pos位置从pre改为now
};

我们只需要写一个操作:

void change(int l,int r,int x,int c) //当前区间为[l,r],把x点更改为c
{
    //TODO:这里维护信息
    if(l<=x&&x<=r)
    {
        //维护改动
    }
    //修改该点 比如col[x]=c;
}

然后我们在solve()函数中扫的时候,扫时间的话就

int T=q[i].ti;
while(ti<T) ++ti,change(l,r,t[T].pos,t[T].now); //没改的时候要改成改后的颜色
while(ti>T) change(l,r,t[T].pos,t[T].pre),ti--; //改过了的话要改回改前的颜色

经检验,对于带修改的莫队算法,分块大小分到\(n^\frac{2}{3}\) 应该是最优的,可以证明,最坏时间复杂度为\(O(n^\frac{5}{3})\)(强行不到\(n^2\)系列。。。)

下面就贴一道例题和代码吧= =
例题:bzoj2120 http://www.lydsy.com/JudgeOnline/problem.php?id=2120

代码:

/**************************************************************
    Problem: 2120
    User: zyan
    Language: C++
    Result: Accepted
    Time:832 ms
    Memory:6840 kb
****************************************************************/

#include <cmath>
#include <vector>
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=50505;
const int C=1010101;
int n,m,blk,opt,qid,tid;
int col[N],last[N],ans[N],ex[N],cur[C];
struct query{
    int l,r,id,ti;
}q[50505];
bool cmp(const query &a,const query &b){
    if(a.l/blk==b.l/blk){
        if(a.r/blk==b.r/blk) return a.ti<b.ti;
        return a.r<b.r;
    }
    return a.l<b.l;
}
struct modify{
    int p,pre,cur;
}t[1010];

inline int gnum(){
    int a=0;char c=getchar();bool f=0;
    for(;(c<'0'||c>'9')&&c!='-'&&c!='Q'&&c!='R';c=getchar());
    if(c=='Q')return -1; if(c=='R')return -2;
    if(c=='-') c=getchar(),f=1;
    for(;c>='0'&&c<='9';c=getchar()) a=(a<<1)+(a<<3)+c-'0';
    if(f) return -a; return a;
}

void change(int l,int r,int x,int c,int &res){
    if(l<=x&&x<=r){
        cur[col[x]]--; if(!cur[col[x]]) res--;
        col[x]=c; if(!cur[col[x]]) res++; cur[col[x]]++;
    }
    else col[x]=c;
}

void fix(int p,int &res){
    if(ex[p]){
        cur[col[p]]--;
        if(!cur[col[p]]) res--;
    }
    else{
        if(!cur[col[p]]) res++;
        cur[col[p]]++;
    }
    ex[p]^=1;
}

void solve(){
    sort(q+1,q+qid+1,cmp);
    int ti=0,l=q[1].l,r=q[1].l-1,res=0;
    for(int i=1;i<=qid;i++){
        int L=q[i].l,R=q[i].r,T=q[i].ti;
        while(ti>T) change(l,r,t[ti].p,t[ti].pre,res),ti--;
        while(ti<T) ti++,change(l,r,t[ti].p,t[ti].cur,res);
        while(l<L) fix(l++,res);
        while(l>L) fix(--l,res);
        while(r>R) fix(r--,res);
        while(r<R) fix(++r,res);
        ans[q[i].id]=res;
    }
}

int main(){
    n=gnum(),m=gnum(); blk=sqrt(n);

    for(int i=1;i<=n;i++) last[i]=col[i]=gnum();

    for(int i=1;i<=m;i++){ opt=gnum();
        if(opt==-1) {int L=gnum(),R=gnum(); q[++qid].l=L,q[qid].r=R,q[qid].id=qid,q[qid].ti=tid;}
        else{int x=gnum(),p=gnum(); t[++tid].p=x; t[tid].pre=last[x]; t[tid].cur=p; last[x]=p;} //这里改的时候一定要记一下last表示最近一次更改后变成了什么颜色
    } solve();
    for(int i=1;i<=qid;i++) printf("%d\n",ans[i]);
}

嗯 就是这样啦~

原文地址:https://www.cnblogs.com/enzymii/p/8412192.html

时间: 2024-10-14 11:24:05

【笔记篇】莫队算法(二)的相关文章

莫队算法学习笔记【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 2038: [2009国家集训队]小Z的袜子(hose)【莫队算法裸题&amp;&amp;学习笔记】

2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 9894  Solved: 4561[Submit][Status][Discuss] Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两

BZOJ 2038: [2009国家集训队]小Z的袜子(hose) [莫队算法]【学习笔记】

2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 7687  Solved: 3516[Submit][Status][Discuss] Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只

莫队算法及其应用

在写这篇博客之前,我最想做的一件事就是:ORZ莫队%%%%%%%%. 说明:ceil(x)表示x向上取整,sqrt(x)表示对x开算数平方根. 一.莫队算法简介 莫队算法是一种暴力算法,真的很暴力,但速度很快,属于速度快的暴力.它的基本思想就是分块.关于分块的介绍建议参考hzwer的博客,然后%%%%hzw.莫队算法主要用于解决一类离线查询的问题,和线段树处理的问题是一样的,但处理的是两个不同的方面,当由[L,R]转移到[L',R']的时间为O(|L'-L|+|R'-R|)时适宜使用莫队算法.这

莫队算法小结(Markdown版)

wtf,最近挖坑有点小多啊,没办法>_<容我先把糖果公园A了再来写这个吧= =看看今天能不能A掉 好吧,我承认我第二天才把糖果公园A掉>_<下面把这篇小结补上 首先众所周知的是莫队算法是要把询问先按左端点属于的块排序,再按右端点排序 复杂度就先不证了,有兴趣的同学可以自己YY下或者查阅资料 下面举几个例子详细说明 1.小Z的袜子 Description: 给定一个序列m个询问 每次询问: 区间中选两个数,两个数相等的概率 若概率为0则输出01 仔细观察发现,令x表示x这个值出现的次

莫队算法~讲解

用了大约1h搞定了基础的莫队算法.写篇博客算是检验下自己的学习成果. 一.什么是莫队算法? 莫队算法是用来处理一类无修改的离线区间询问问题.——(摘自前国家队队长莫涛在知乎上对莫队算法的解释.) 莫队算法是前国家队队长莫涛在比赛的时候想出来的算法. 传说中能解决一切区间处理问题的莫队算法. 准确的说,是离线区间问题.但是现在的莫队被拓展到了有树上莫队,带修莫队(即带修改的莫队).这里只先讲普通的莫队. 还有一点,重要的事情说三遍!莫队不是提莫队长!莫队不是提莫队长!!莫队不是提莫队长!!! 二.

(普通的)莫队算法简单介绍

莫队算法(由莫涛发明的)是一种离线的暴力算法(至少我这么认为).使用莫队算法的条件是,知道一个区间[l, r]的结果,那么也可以快速知道[l + 1, r],[l - 1, r], [l, r - 1], [l, r + 1]这四个区间的结果.于是可以想到,直接通过这样转移来解决一些问题.当然有些出题人机智,故意卡这种暴力,让你从头跑到尾然后从尾跑到头,于是时间复杂度高达O(n2) 而莫队算法就是通过改变处理询问的顺序来降低时间复杂度. 比如说现在知道一个区间[l1, r1],又要转移到[l2,

莫队算法良心讲解

问题:有n个数组成一个序列,有m个形如询问L, R的询问,每次询问需要回答区间内至少出现2次的数有哪些. 朴素的解法需要读取O(nm)次数.如果使用STL的Map来保存出现的次数,每次需要O(nmlogn)的复杂度.有没有更快的方法呢? 注意到询问并没有强制在线,因此我们可以使用离线方法.注意到一点,如果我们有计算完[L, R]的map,那么[L - 1, R].[L + 1, R].[L, R - 1].[L, R + 1]都能够在O(logn)的复杂度得出.是否能安排适当的询问顺序,使得每次

清橙A1206 小Z的袜子(莫队算法)

A1206. 小Z的袜子 时间限制:1.0s   内存限制:512.0MB 总提交次数:744   AC次数:210   平均分:44.44 将本题分享到: 查看未格式化的试题   提交   试题讨论 试题来源 2010中国国家集训队命题答辩 问题描述 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命-- 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是