AT1219 歴史の研究(回滚莫队)

题目描述

IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记。JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件。

日记中记录了连续N天发生的时间,大约每天发生一件。

事件有种类之分。第i天(1<=i<=N)发生的事件的种类用一个整数Xi?表示,Xi?越大,事件的规模就越大。

JOI教授决定用如下的方法分析这些日记:

选择日记中连续的一些天作为分析的时间段

事件种类t的重要度为t*(这段时间内重要度为t的事件数)

计算出所有事件种类的重要度,输出其中的最大值 现在你被要求制作一个帮助教授分析的程序,每次给出分析的区间,你需要输出重要度的最大值。

1<=N<=105,1<=Q<=105

1<=Xi?<=109(1<=i<=N)

题解

这类问题很明显可以用莫队,可是注意到本题用莫队只支持扩张区间,而不能缩短区间。

所以就要引用一种神奇的做法:回滚莫队。

回滚莫队和普通莫队一样需要先分块再将询问排序;不同的只是询问左端点在同一个块时,每次将左端点放在该块的右边界,因为右端点单增,所以先扩张右端点记录答案,再扩张左端点询问答案,最后再让左端点滚回有边界。

具体如何实现?

对于每个块需要从头开始统计答案,右端点扩张时对于这个块还没赋予答案的询问都有贡献,当左端点扩张前要记录当前答案,最后回到记录的答案以使得答案也回滚。

本题要注意的是需要离散化;对于询问端点在同一块内时,暴力统计即可;块的大小size不等于块的个数pos[n],就因为这个WA了好久。。

#include<bits/stdc++.h>
using namespace std;

#define ll long long
const int maxn=100005;
int n,m;
ll aa[maxn],a[maxn],b[maxn];//原序列,离散化序列,桶
int size,pos[maxn];
ll ans[maxn],nowans;
struct question{
    int l,r,id;
}q[maxn];

template<class T>inline void read(T &x){
    x=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
}

ll max(ll x,ll y){return x>y ? x : y ;}

bool cmp(question a,question b){
    if(pos[a.l]==pos[b.l]) return a.r<b.r;
    return pos[a.l]<pos[b.l];
}

ll cx[maxn];//暴力使用 

ll nice(int l,int r){
    for(int i=l;i<=r;i++) cx[a[i]]=0;
    ll ret=0;
    for(int i=l;i<=r;i++){
        cx[a[i]]++;
        ret=max(ret,aa[a[i]]*cx[a[i]]);
    }
    return ret;
}

void add(int x){
    b[a[x]]++;
    nowans=max(nowans,aa[a[x]]*b[a[x]]);
}

void del(int x){
    b[a[x]]--;
}

int mo(int i,int id){
    int R=min(n,id*size),nowl=R,nowr=nowl-1;
    nowans=0;
    memset(b,0,sizeof(b));
    for(;pos[q[i].l]==id;i++){
        if(pos[q[i].l]==pos[q[i].r]) {ans[q[i].id]=nice(q[i].l,q[i].r);continue;}
        while(nowr<q[i].r) add(++nowr);
        ll ret=nowans;
        while(nowl>q[i].l) add(--nowl);
        ans[q[i].id]=nowans;
        while(nowl<R) del(nowl++);
        nowans=ret;
    }
    return i;
}

int main(){
    read(n);read(m);
    size=sqrt(n+0.5);
    for(int i=1;i<=n;i++){read(aa[i]);a[i]=aa[i];pos[i]=(i-1)/size+1;}
    sort(aa+1,aa+n+1);
    int tot=unique(aa+1,aa+n+1)-aa-1;
    for(int i=1;i<=n;i++) a[i]=lower_bound(aa+1,aa+tot+1,a[i])-aa;
    for(int i=1;i<=m;i++){read(q[i].l);read(q[i].r);q[i].id=i;}
    sort(q+1,q+m+1,cmp);
    for(int i=1,id=1;id<=pos[n];id++)
     i=mo(i,id);
    for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
}

原文地址:https://www.cnblogs.com/sto324/p/11235729.html

时间: 2024-09-28 02:30:52

AT1219 歴史の研究(回滚莫队)的相关文章

习题:历史研究(回滚莫队)

题目 传送门 思路 很版的一道回滚莫队的题 我们如果用普通的莫队,我们发现最难维护的是最大值, 因为你无法预测缩减时最大值的变化,还要带一个线段树或者什么来维护 时间复杂度为\(O(n*log_n*\sqrt n)\) 但是我们想,我们如果已知一个莫队的左端点和右端点以及它的最大值 那么这个莫队向外拓展我们是很容易维护的 之后如果下一个操作也是向外拓展就向外拓展,如果是内缩, 我们就将这个莫队还原成为我们最开始已知的样子,在进行拓展 这也就是回滚莫队的主要思想,这道题也是如此 时间复杂度依然也是

Codeforces 620F Xors on Segments 回滚莫队 + 字典树 || 离心询问分治 + 可持久化字典树

Xors on Segments 转换一下变成询问区间选两个数异或的最大值, 要注意的是一个数作为左端点要-1, 所以在回滚莫队的时候用两棵字典树维护. 这个题居然n ^ 2 也能过...  其实用分治 + 可持久化字典树可以做到n * log(n) * log(n), 懒得写了... #include<bits/stdc++.h> #define LL long long #define LD long double #define ull unsigned long long #defin

[CSP-S模拟测试]:ants(回滚莫队)

题目描述 然而贪玩的$dirty$又开始了他的第三个游戏. $dirty$抓来了$n$只蚂蚁,并且赋予每只蚂蚁不同的编号,编号从$1$到$n$.最开始,它们按某个顺序排成一列.现在$dirty$想要进行$m$场比赛,每场比赛给出$l$和$r$,表示选出从左向右数第$l$只至第$r$只蚂蚁.被选出的蚂蚁需要快速地按编号从小到大排序,之后这些蚂蚁中编号连续的蚂蚁将围成一个圈.每场比赛结束后,蚂蚁们还需要快速地回到最开始的位置. 按照蚂蚁的审美标准,围成的圈越大美观值就越大.于是$dirty$每次需要

AT1219 歴史の研究

题意 关于回滚莫队见这篇博客. code: #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+10; const int maxt=350; int n,Q,t,num,block,nowl,nowr; int a[maxn],b[maxn],L[maxt],R[maxt],pos[maxn],cnt[maxn],tmpcnt[maxn]; ll nowans; ll an

「AtCoder 1219」歴史の研究

给出 n 个整数 \(x_1, x_2, ...,x_n\) ,询问 [l, r] 中 max{\(x_k\times cnt_{x_k}\)}( \(cnt_i\) 表示 i 出现的次数) Luogu AtCoder 分析 回滚莫队裸题. 当然也可以用分块做,但我一开始打的分块,成功的只过了 4 个点......没调出来...... 顺便贴一下官方题解,虽然是日文 歴史の研究 代码 回滚莫队 //========================= // author:hliwen // dat

【AT1219】历史研究

题面 题目描述 \(IOI\)国历史研究的第一人--\(JOI\)教授,最近获得了一份被认为是古代\(IOI\)国的住民写下的日记.\(JOI\)教授为了通过这份日记来研究古代\(IOI\)国的生活,开始着手调查日记中记载的事件. 日记中记录了连续\(N\)天发生的时间,大约每天发生一件. 事件有种类之分.第\(i\)天\((1<=i<=N)\)发生的事件的种类用一个整数\(X_i\)表示,\(X_i\)越大,事件的规模就越大. \(JOI\)教授决定用如下的方法分析这些日记: 选择日记中连续

loj#6285 数列分块入门 9 ( 回 滚 )

题目 :  链接 :https://loj.ac/problem/6285 题意:给出一个长为 n的数列,以及 n个操作,操作涉及询问区间的最小众数. 思路:虽然这不是一道 回滚莫队题,就是 暴力分块 的题, 但是 还是 可以用回滚莫队 写滴,好像大部分题解都是 暴力分块. #include<bits/stdc++.h> #define LL long long #define ULL unsigned long long #define rep(i,j,k) for(int i=j;i<

BZOJ4241 历史研究 莫队算法 堆

欢迎访问~原文出处--博客园-zhouzhendong&AK 去博客园看该题解 题目 Description IOI国历史研究的第一人--JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记.JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件. 日记中记录了连续N天发生的时间,大约每天发生一件. 事件有种类之分.第i天(1<=i<=N)发生的事件的种类用一个整数Xi表示,Xi越大,事件的规模就越大. JOI教授决定用如下的方法分析这些日记: 1.

oracle回滚机制深入研究

这篇文章主要描述oracle的回滚机制,篇幅可能较长,因为对于oracle的回滚机制来说,要讨论和描述的实在太多,只能刷选自己认为最有意义的一部分进行深入研究和分享 一.我们来看一个DML语句的处理过程描述 update undotest set object_type='VIEW' where object_type='PROCEDURE'; 检查shared pool中是否存在相同的语句,如果存在,重用执行计划,执行扫描运算,如果不存在,执行硬解析生成执行计划 根据执行计划中的扫描运算,检查