LibreOJ 6285. 数列分块入门 9

题目链接:https://loj.ac/problem/6285

其实一看到是离线,我就想用莫队算法来做,对所有询问进行分块,但是左右边界移动的时候,不会同时更新数字最多的数,只是后面线性的扫了一遍,所以还有百分之12的样例过不了。

然后看了别人分块,是先对所有零散的数字编号(这个应该是所谓离散化),用vector[i]存储编号为i的数字所有出现的位置,因为从0到n,所以里面的值是升序的,我们先对块与块之间数字最多的数进行计算(预处理),在查询的时候查询[l,r]之间的数,把区间分成三块,左边不完整的块,中间完整的块,后面不完整的块,对于不完整的块可以遍历每一个元素用二分查找相应编号的vector里面在这个范围的数字有多少,完整的块就直接把预处理的数字拿出来。我做这题超时无数次,建议就是尽量减少map的调用,在查找时对于找过的数字编号可以标记,下次不找,然后块的大小可以设成block=80,我做的时候就是因为map调用太多,块的大小一直是sqrt(n),然后一直超时。

代码比较丑:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<vector>
#include<cstdio>
using namespace std;
typedef long long LL;
#define eps 1e-8
#define INF 0x3f3f3f3f
#define maxn 100010
int a[maxn],num[maxn],MAX[2010][2010],lump[maxn],val[maxn];//num计数,MAX记录块与块之间最多的数字对应编号
// lump记录组,val保存原始值
vector<int>ve[maxn];
bool vis[maxn];//标记
map<int,int>mp;
int n,m,k,t,block,id;
void cal(int x)//预处理
{
    int count1=0,max1=0;
    memset(num,0,sizeof(num));
    for(int i=(x-1)*block+1;i<=n;i++)
    {
        int s=a[i];
        num[s]++;
        if(num[s]>count1||num[s]==count1&&val[s]<val[max1])
        {
            count1=num[s];
            max1=s;
        }
        MAX[x][lump[i]]=max1;
    }
}
int ask(int x,int l,int r)//二分查找
{
    return upper_bound(ve[x].begin(),ve[x].end(),r)-lower_bound(ve[x].begin(),ve[x].end(),l);
}
int find(int l,int r)
{
    memset(vis,0,sizeof(vis));
    int ans=0,count1=0;
    ans=MAX[lump[l]+1][lump[r]-1];
    count1=ask(ans,l,r);
    vis[ans]=true;//记录编号为ans的数字查找过
    for(int i=l;i<=min(lump[l]*block,r);i++)
    {
        if(vis[a[i]])
        continue;
        vis[a[i]]=true;
        int count2=ask(a[i],l,r);
        if(count2>count1||count1==count2&&val[a[i]]<val[ans])
        {
            count1=count2;
            ans=a[i];
        }
    }
    if(lump[l]!=lump[r])
    {
        for(int i=(lump[r]-1)*block+1;i<=r;i++)
        {
            if(vis[a[i]])
            continue;
            vis[a[i]]=true;
            int count2=ask(a[i],l,r);
            if(count2>count1||count1==count2&&val[a[i]]<val[ans])
            {
                count1=count2;
                ans=a[i];
            }
        }
    }
    return val[ans];
}
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
int main()
{
    scanf("%d",&n);
    block=80;//这里很多人都写了80,。。。。。。
    id=0;
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        lump[i]=(i-1)/block+1;
        if(mp[a[i]]==0)
        {
            mp[a[i]]=++id;
            val[id]=a[i];//保存原始值
        }
        a[i]=mp[a[i]];//这个是保存编号,减少map调用,之前我没有这个一直超时
        ve[a[i]].push_back(i);
    }
    for(int i=1;i<=lump[n];i++)
    cal(i);
    int l,r;
    for(int j=1;j<=n;j++)
    {
        l=read();
        r=read();
        printf("%d\n",find(l,r));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/6262369sss/p/9789228.html

时间: 2024-08-30 12:09:08

LibreOJ 6285. 数列分块入门 9的相关文章

LOJ6277~6285 数列分块入门

Portals 分块需注意的问题 数组大小应为\(N+\sqrt N\),因为最后一个块可能会超出\(N\)的范围.改成记录\(blk,fr,to\)就不用担心这个了 当操作的区间在一个块内时,要特判成暴力修改. 要清楚什么时候应该+tag[t] 最后一个块是越界的,注意是否有影响 数列分块入门 1 给出一个长为\(n\)的数列,以及\(n\)个操作,操作涉及区间加法,单点查值. //数列分块入门 1 #include <cstdio> #include <cmath> inlin

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<

LibreOJ 6278. 数列分块入门 2 题解

题目链接:https://loj.ac/problem/6278 题目描述 给出一个长为 \(n\) 的数列,以及 \(n\) 个操作,操作涉及区间加法,询问区间内小于某个值 \(x\) 的元素个数. 输入格式 第一行输入一个数字 \(n\). 第二行输入 \(n\) 个数字,第 \(i\) 个数字为 \(a_i\),以空格隔开. 接下来输入 \(n\) 行询问,每行输入四个数字 \(opt\).\(l\).\(r\).\(c\),以空格隔开. 若 \(opt=0\),表示将位于 \([l,r]

Loj 6285. 数列分块入门 9

链接: https://loj.ac/problem/6285 思路: 离散化处理下就好了,具体解释在代码里. ps: 小新新别看了,你学不会的 实现代码: #include<bits/stdc++.h> using namespace std; const int M = 1e5 + 10; int n,block,idx,a[M],bl[M],f[510][510],val[M],cnt[M]; vector<int>ve[M]; void pre(int x){ //预处理

LibreOJ 6277. 数列分块入门 1

题目链接:https://loj.ac/problem/6277 参考博客:https://www.cnblogs.com/stxy-ferryman/p/8547731.html 两个操作,区间增加和单点查询. 思路:将整个数组按照block(block=sqrt(n))分成许多小块,lump[i]表示点i所在的块,tag[i]表示编号为i的块的增加值,如果是进行区间增加操作,我们一般可以把区间[l,r]分成三个部分,左边不完整的区间(只含有某块中的部分点),中间完整的区间(含有一些块的所有点

LibreOJ 6282. 数列分块入门 6

题目链接:https://loj.ac/problem/6282 参考博客:http://www.cnblogs.com/stxy-ferryman/p/8560551.html 这里如果用数组的话元素右移肯定会超时,如果用链表查询时O(n),n次询问就是O(n^2),然后刚刚又瞟了几眼别人的博客,用分块的话主要好像是有查询位置,插入元素,重构三个操作,查询就是找我们要的这个点在第几层的第几个位置(用的是vector),大概是√n的时间复杂度,因为分成了√n块:然后找到位置之后就可以插入,也是√

数列分块入门1-9 LibreOJ

数列分块入门1-9 LibreOJ 我也不知道为什么一个大二的ACM选手没学分块. 我怎么记得大一的时候,学长教给我的分块就只有 block 和 num 两个变量来着...好吧,应该是我没认真学.正好前两天朋友给学弟开课,乘机去蹭了一节课.然后...我还是不会哇,菜的一逼塌糊涂. 还是卿学姐好哇,多听几遍,睡得贼香. 分块原理 分块嘛,其实就是优雅的暴力,和莫队(不会)有点异曲同工的赶脚.通过将数组分成小块以降低复杂度. 通常情况下: 每个块的大小(block)为 \(\sqrt{n}\) 块数

loj 6278 6279 数列分块入门 2 3

参考:「分块」数列分块入门1 – 9 by hzwer 2 Description 给出一个长为\(n\)的数列,以及\(n\)个操作,操作涉及区间加法,询问区间内小于某个值\(x\)的元素个数. 思路 每个块内保持升序排列. 则块外暴力统计,块内二分查找分界点. 一些注意点,如: 要记录下标: 块外暴力修改完之后需要再排序: 在块内二分查找的值是\(c-tag[i]\)而非\(c\). Code #include <bits/stdc++.h> #define maxn 50010 #def

loj 6277 6280 数列分块入门 1 4

参考:「分块」数列分块入门1 – 9 by hzwer 1 Description 给出一个长为\(n\)的数列,以及\(n\)个操作,操作涉及区间加法,单点查值. 思路 用\(tag\)记录每个块整体的增量. Code #include <bits/stdc++.h> #define maxn 50010 #define F(i, a, b) for (int i = (a); i < (b); ++i) #define F2(i, a, b) for (int i = (a); i