题目

题目大意

给你一个数列,有很多个询问,询问一段区间内,某个数乘它的出现次数的最大值,也就是带权众数。


思考历程

第一次看到这道题,立马想到了树套树之类的二位数据结构,发现不行。(就算可以也很难打……)
然后我就想到了莫队!
其实这题的莫队是很显然的。我们用莫队的方法来搞,用一个数据结构来维护目前的答案。
所以我就打出来了时间复杂度为O(mnlg⁡n)O(m \sqrt n \lg n)O(mn​lgn)的做法。
还挺好打的。
交上去之后,我发现,诶,怎么运行这么久?难道是被卡了?
后来看到分数之后……WA,0分。
什么鬼?
然后我就惊奇地发现这题要开long long……
开了之后,诶,40分?
怎么还是这么低的分数(某YMQ用这种方法AC了这题)?
检查程序,发现自己莫队的排序相当于没有排序。
我们知道朴素的莫队做法就是将左端点所在的块为第一关键字,以右端点为第二关键字来排的。
比较函数一般设为:be[x.l]<be[y.l] || be[x.l]==be[y.l] && x.r<y.r
然后我把||打成了&&
感觉自己心态爆炸……这样子和没有排序有什么区别?
改过来之后,60分,嗯,题解分数。
但我不甘心,因为还有两个点是WA!
经过调试之后,我发现是数组越界了,再改改就80分了。
可是还没到100分,因为这不是正解。但YMQ已经卡到100分了,我才不信这个邪。
所以呢,我又卡了一下:
首先将普通的线段树转化为zkw线段树(因为只有单点修改和整体查询,所以还是比较简单的),然后用宏来打了max函数,因为自带的那个是挺慢的。
然后就卡过了!比YMQ快!看看YMQ的程序,嘿嘿,还开了O3……
YMQ日常吸臭氧……


正解

目前我知道的正解有两种,时间复杂度都是O(mn)O\left(m \sqrt n\right)O(mn​),少了一个lg⁡n\lg nlgn
第一种是XZB大佬首创的莫队加桶维护的方法:
莫队的部分是一模一样的,重点是桶。
我们知道莫队排序的时候,按照左端点所在的块为第一关键字,以右端点为第二关键字。
显然,当左端点所在块一样的时候,右端点是递增的。
我们在处理的时候,用桶记录一下当前块后面的信息。
然后,对于在块内的那一部分区间,我们就暴力计算。计算了之后合并两边的信息,求出这一问的答案。
现在有一个问题,怎么合并呢?
这个问题困扰了我一段时间。然后,我发现,反正这个时间复杂度就这样了,所以暴力一点没有关系。
我们先不要理在块中的部分,先统计好块后面的信息,记录这时的带权众数。
然后将块内的数加进桶中,做完之后这时的带权众数就是答案。
最后,我们将桶还原,就是将原来属于这个块内的全部剔除,带权众数还是之前的那个带权众数。
这个是我自己脑补出来的想法,可能其他人还有更加优秀的方法吧。
一直怎么做下去,如果一个块处理完了,就将桶清空,然后继续处理下一个块。
时间复杂度是显然的。

然后还有一种做法叫作分块,我还没有打过,只是思想理解了而已。
我们可以用和上面有点类似的思想:将一段区间内的东西用桶存起来,存下此时的答案,然后再试着将区间外的东西暴力加进去,得出目前的答案,最后还原。
我们可以分成n\sqrt nn​个块,对于每个块,我们将这个块之前的所有东西放在一个桶中。(就像是前缀和)
然后,我们对于每两个块之间的部分,预处理出它们的带权众数。(用一个n∗n\sqrt n *\sqrt nn​∗n​的一个数组来存就好,预处理的时候直接枚举从哪个块开始,然后往后面扫,时间O(nn)O(n \sqrt n)O(nn​))
询问一个区间的时候,这个区间被拆成一个大块和两个散块。对于大块,我们已经预处理除它的带权众数,并且我们通过相减的方式得出一个新的桶。对于散块,我们将里面的元素暴力加进桶中,得出最终的带权众数。
然后你会惊奇地发现,如果真的是这么做,那肯定TLE。
为什么?其实耗费时间的就在一个地方:我们将两个桶相减得出一个新的桶,实际上没有必要。
设后面的桶为aaa,前面的桶为bbb,那么你可以新建一个桶ccc。ccc一开始是全零的。
然后,在后面加数的时候,“新桶”相当于是a−b+ca-b+ca−b+c。在加某个数xxx的时候,我们只需要修改cxc_xcx​,用ax−bx+cxa_x-b_x+c_xax​−bx​+cx​来统计答案。做完了之后,将这些数从ccc中一一减去,然后ccc就清零了(如果直接清零是会TLE的)。
这个时间复杂和上面的一样,不过感觉上面的好打一些。


代码

O(mnlg⁡n)O(m \sqrt n \lg n)O(mn​lgn)的水法

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 252144
int n,m,col;
int a[N+1],p[N+1],b[N+1];//a表示原数组,b表示离散化后的数组
inline bool cmpp(const int x,const int y){return a[x]<a[y];
}
int K;
int be[N+1];//表示所在的块
struct Oper{int l,r;int num;
} o[N+1];
inline bool cmp(const Oper &x,const Oper &y){return be[x.l]<be[y.l] || be[x.l]==be[y.l] && x.r<y.r;//比较函数千万不要打错了……我当时就是在这里GG的
}
long long t[N*2+1];
int M;
inline void add(int,int);
long long ans[N+1];
int main(){scanf("%d%d",&n,&m);K=sqrt(n);for (int i=1;i<=n;++i)scanf("%d",&a[i]),p[i]=i;//以下是离散化sort(p+1,p+n+1,cmpp);for (int i=1,bef=0;i<=n;++i){if (a[p[i]]!=bef)bef=a[p[i]],col++;b[p[i]]=col;}for (M=1;M<col;M<<=1);for (int i=1;i<=m;++i)scanf("%d%d",&o[i].l,&o[i].r),o[i].num=i;for (int i=1;i*K<=n;++i)for (int j=0;j<K && i*K+j<=n;++j)be[i*K+j]=i;//以下是莫队sort(o+1,o+m+1,cmp);int l=1,r=0;for (int i=1;i<=m;++i){for (;r<o[i].r;r++)add(r+1,1);for (;l>o[i].l;l--)add(l-1,1);for (;r>o[i].r;r--)add(r,-1);for (;l<o[i].l;l++)add(l,-1);ans[o[i].num]=t[1];}for (int i=1;i<=m;++i)printf("%lld\n",ans[i]);return 0;
}
#define my_max(x,y) (((x)>(y))?(x):(y))
inline void add(int x,int c){//zkw线段树中的int k=b[x]+M;t[k]+=c*a[x];for (k>>=1;k;k>>=1)t[k]=my_max(t[k<<1],t[k<<1|1]);
}

O(mn)O(m\sqrt n)O(mn​)的莫队加桶做法

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 252144
int n,m,col;
int a[N+1],p[N+1],b[N+1];
inline bool cmpp(const int x,const int y){return a[x]<a[y];
}
int K;
int be[N+1],rig[N+1];
struct Oper{int l,r;int num;
} o[N+1];
inline bool cmp(const Oper &x,const Oper &y){return be[x.l]<be[y.l] || be[x.l]==be[y.l] && x.r<y.r;
}
long long buc[N+1],rmx;
long long ans[N+1];
#define MAX(a,b) (((a)>(b))?(a):(b))
int main(){//  freopen("in.txt","r",stdin);
//  freopen("out.txt","w",stdout);scanf("%d%d",&n,&m);K=sqrt(n);for (int i=1;i<=n;++i)scanf("%d",&a[i]),p[i]=i;sort(p+1,p+n+1,cmpp);for (int i=1,bef=0;i<=n;++i){if (a[p[i]]!=bef)bef=a[p[i]],col++;b[p[i]]=col;}for (int i=1;i<=m;++i)scanf("%d%d",&o[i].l,&o[i].r),o[i].num=i;for (int i=0;1+i*K<=n;++i)for (int j=1;j<=K && i*K+j<=n;++j)be[i*K+j]=i+1;be[0]=0;for (int i=1;i<=n;++i)rig[be[i]]=i;sort(o+1,o+m+1,cmp); for (int i=1,r=0;i<=m;++i){if (be[o[i-1].l]!=be[o[i].l]){memset(buc,0,sizeof buc);rmx=0;r=rig[be[o[i].l]]+1;for (;r>o[i].r;--r)buc[b[r-1]]-=a[r-1];//防止左端点和右端点在同一个块中的情况}for (;r<=o[i].r;++r)buc[b[r]]+=a[r],rmx=MAX(rmx,buc[b[r]]);//右端点往外延伸long long tmx=rmx;//由于等一下要还原,所以这个值要暂时记录for (int l=rig[be[o[i].l]];l>=o[i].l;--l)buc[b[l]]+=a[l],tmx=MAX(tmx,buc[b[l]]);//将块内的暴力记录在桶中ans[o[i].num]=tmx;for (int l=rig[be[o[i].l]];l>=o[i].l;--l)//还原buc[b[l]]-=a[l];}for (int i=1;i<=m;++i)printf("%lld\n",ans[i]);return 0;
}

其实这题的数据很水……我一开始的正解程序并没有判断左右端点在同一个块中的特殊情况,可我还是AC了。


总结

如果见到一些用lg⁡\lglg做法不好维护的东西,那就试一下分块和莫队。
从这题当中,我们也得到了一个用来优化莫队的思想,就是将块后和块中的分别考虑,有时会有非常好的成果。
最后就是,卡常技巧很重要,说不定你可以用次解来AC这道题。不要像YMQ一样天天吸臭氧。

转载于:https://www.cnblogs.com/jz-597/p/11145246.html

[JZOJ4763] 【NOIP2016提高A组模拟9.7】旷野大计算相关推荐

  1. 【JZOJ4763】【NOIP2016提高A组模拟9.7】旷野大计算

    题目描述 输入 输出 样例输入 5 5 9 8 7 8 9 1 2 3 4 4 4 1 4 2 4 样例输出 9 8 8 16 16 数据范围 解法 离线莫队做法 考虑使用莫队,但由于在删数的时候难以 ...

  2. JZOJ 100024. 【NOIP2016提高A组模拟7.6】数球

    Description 小A有n个球,编号分别为1到n,小A每次都会从n个球中取出若干个球,至少取一个,至多取n个,每次取完再放回去,需要满足以下两个条件. 每次取出的球的个数两两不同. 每次取出的球 ...

  3. JZOJ4708. 【NOIP2016提高A组模拟8.20】奇洛金卡达 倒着做的思想+并查集维护

    题目大意 给定一个长度小于等于kk的字符串和qq,表示现在有qq个操作,每个操作给定两个参数li,ril_i,r_i表示把现在的字符串第lil_i到rir_i把其中编号为奇数的按顺序写下来,再在后面把 ...

  4. 【NOIP2016提高A组模拟9.9】闭门造车

    题目 自从htn体验了一把飙车的快感,他就下定决心要闭门造车!但是他两手空空怎么造得出车来呢?无奈的他只好来到了汽车零部件商店. 一走进商店,玲琅满目的各式零件看得htn眼花缭乱.但是他很快便反应过来 ...

  5. [JZOJ4788] 【NOIP2016提高A组模拟9.17】序列

    题目 描述 题目大意 一个序列,每次可以使一段区间内的所有数加一(模四). 问最少的操作次数. 思考历程 一看这题目,诶,这不就是那道叫密码锁的题目吗? 然后随便打一打,样例过了,就再也没有思考这一题 ...

  6. JZOJ 4726. 【NOIP2016提高A组模拟8.22】种花

    Description 经过三十多个小时的长途跋涉,小Z和小D终于到了NOI现场--南山南中学.一进校园,小D就被花所吸引了(不要问我为什么),遍和一旁的种花园丁交(J)流(L)了起来. 他发现花的摆 ...

  7. JZOJ 4676. 【NOIP2016提高A组模拟7.21】模板串

    Description 科学家温斯顿从数据库中找到了一串相当长的字符串. 他正试图用一个模板串来重构这个字符串. 他可以将模板串复制多份,通过合适的方式拼接起来,使得最终的串与原串一致. 如果两个模板 ...

  8. JZOJ 4675. 【NOIP2016提高A组模拟7.21】Double-row

    Description 科学家温斯顿在一张超长的白纸上写下了两行数,每一行数有N个. 但他写完后觉得看起来有点不和谐.他希望重新编排,使得每一行数中没有相同的数. 他每次可以调换同一列的两个数. 请帮 ...

  9. JZOJ 4786. 【NOIP2016提高A组模拟9.17】小a的强迫症

    Description Input Sample Input 3 2 2 1 Output Sample Output 3 样例解释: Data Constraint Solution 首先,我们设之 ...

最新文章

  1. Magento开发的特点有哪些?
  2. iptables总结
  3. Navicat怎样筛选数据
  4. 【CyberSecurityLearning 42】日志记录规则
  5. #define const typedef
  6. Leet Code OJ 3. Longest Substring Without Repeating Characters
  7. navicat创建计算列_Tableau Part 9 计算字段amp;粒度聚合比率amp;表计算
  8. 求解10的75次方问题
  9. DevOps通用及版本控制面试题
  10. 分布式事务方案这么多,到底应该如何选型?
  11. Python运算符+与+=的那些事
  12. 神奇的go语言(面向对象)
  13. 举例HTML的图像标记,教案html之css滤镜及练习层div块及span标记举例窗口内例题演示功能的实现总结.pdf...
  14. Mac 如何保护您的数据安全?
  15. 第十一讲 在ASP.NET中使用用户控件
  16. OFFICE拼写语法检查:全部忽略、全部更正的功能
  17. 如何在excel中打钩
  18. python 计算器功能实现
  19. VB.net 调用FFmpeg简单处理视频(类库——6)
  20. 操作系统——进程管理(进程的概念与进程控制)

热门文章

  1. 讲解创业企业如何选择企业邮箱
  2. python基础--thirteen
  3. weka dblp数据集挖掘
  4. 美瞳赛道,只做得好看跑不赢
  5. 网易视频云谈视频云的根基:云计算的介绍
  6. 100之内含有7与7的倍数的数
  7. 微信公众号推广技巧之一
  8. 微信MAC最新版3.1.0支持发朋友圈了
  9. 博弈论——2.4古诺模型练习题
  10. springcloud(十一):服务网关 Spring Cloud GateWay 入门