• 【题解】BZOJ4241: 历史研究(魔改莫队)


    【题解】BZOJ4241: 历史研究(魔改莫队)

    真的是好题啊

    题意

    给你一个序列和很多组询问(可以离线),问你这个区间中(max){元素出现个数( imes)元素权值}

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

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

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

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

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

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

    3. 计算出所有事件种类的重要度,输出其中的最大值

    现在你被要求制作一个帮助教授分析的程序,每次给出分析的区间,你需要输出重要度的最大值。

    题解

    和区间元素出现个数有关,先考虑一下莫队。莫队维护了元素出现个数,现在要知道元素( imes)权值最大值。

    可以发现,你向莫队记录的答案中添加数是可以的,可以正确维护最大值(请意会:ans=max(ans,(ll)data[i]*cnt[i]);),但是对于要删除元素来说不行(你莫队套别的数据结构也不行,超时),所以我们的莫队仅仅支持添加元素。

    只能够添加元素的莫队?那能不能魔改一下莫队?

    考虑将询问按照莫队一样的方式将询问分块,总共有(O(sqrt n))块,每一块询问相互独立。对于每一块询问,和莫队一样,按照右端点升序排序。

    • 先考虑一个询问右端点怎么搞定: 我们搞个指针一直向右增加,维护一个(cnt[])数组。这样的复杂度是(O(n))的,因为这个指针不会回退。

    • 再考虑左端点怎么搞定: 直接暴力统计。我们让之前那个指针从当前块右端点出发向右。现在块右端点右边边的(cnt[])可以得到了。

      但是我们还差块右端点左边的贡献。这部分以之前那个(cnt[])为基础直接暴力统计,因为这里仍然是只向莫队添加元素,所以可以统计答案。由于是莫队的方式询问分块,对于每个询问,暴力统计的复杂度不超过(O(sqrt n))

    但是可能询问区间就小于(sqrt n),为了防止我们讨论边界情况,这部分询问直接读入的时候就处理了。

    分析一下复杂度:

    • 对于每个询问,暴力统计部分是(O(sqrt n)),这部分复杂度(O(nsqrt n))
    • 对于每个块,指针向右移动(O(n))的,这部分复杂度(O(nsqrt n))
    • 对于那种小询问,暴力统计(O(nsqrt n))
    • 其实要离散化,但是我们不需要按照大小离散化,直接哈希(O(n)) 。(bzoj不支持,只能用map)

    总复杂度(O(nsqrt n))

    这道题实际上提供了如何写只支持添加/删除操作的莫队的算法。普适性很广。

    //@winlere
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<cmath>
    #include<map>
    
    using namespace std;  typedef long long ll;
    inline int qr(){
          register int ret=0,f=0;
          register char c=getchar();
          while(c<48||c>57)f|=c==45,c=getchar();
          while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
          return f?-ret:ret;
    }
    
    struct E{
          int l,r,id;
          E(){l=r=0;}
          E(const int&a,const int&b,const int&c){l=a;r=b;id=c;}
          inline bool operator <(const E&a)const{return r<a.r;}
    };
    const int maxn=1e5+5;
    int cnt0,n,m,N;
    int be[maxn],cnt[maxn],arc[maxn],data[maxn];
    ll ans[maxn],now;
    map<int,int> s0;
    vector<int> s;
    vector<E> ve[355];
    
    inline void add(const int&pos,const int&tag){
          cnt[data[pos]]+=tag;
          if(1ll*cnt[data[pos]]*arc[data[pos]]>now) now=1ll*cnt[data[pos]]*arc[data[pos]];
    }
    
    int main(){
          n=qr(); m=qr(); N=sqrt(n-1)+1;
          for(register int t=1;t<=n;++t){
    	    data[t]=qr();
    	    if(s0.find(data[t])==s0.end()) s0[data[t]]=++cnt0,arc[cnt0]=data[t];
    	    data[t]=s0[data[t]];
          }
          for(register int t=1;t<=n;++t) be[t]=(t-1)/N+1;
          for(register int t=1,t1,t2;t<=m;++t) {
    	    t1=qr(),t2=qr();
    	    if(t2-t1-5<N){
    		  now=0;
    		  for(register int t=t1;t<=t2;++t) add(t,1),s.push_back(data[t]);
    		  for(register int t=0,ed=s.size();t<ed;++t) --cnt[s[t]];
    		  ans[t]=now; s.clear();
    		  continue;
    	    }
    	    ve[be[t1]].push_back(E(t1,t2,t));
          }
          for(register int t=1;t<=be[n];++t){
    	    if(ve[t].empty()) continue;
    	    memset(cnt,0,sizeof cnt);
    	    sort(ve[t].begin(),ve[t].end());
    	    int R=N*t,st=R; ll tnow=now=0;
    	    for(register int i=0,ed=ve[t].size();i<ed;++i){
    		  register E f=ve[t][i];
    		  while(R<f.r) add(++R,1);
    		  tnow=now;
    		  for(register int t=st;t>=f.l;--t) add(t,1),s.push_back(data[t]);
    		  for(register int t=0,ed=s.size();t<ed;++t) --cnt[s[t]];
    		  ans[f.id]=now; now=tnow; s.clear();
    	    }
          }
          for(register int t=1;t<=m;++t) printf("%lld
    ",ans[t]);
          return 0;
    }
    
    
  • 相关阅读:
    数据库练习题
    支付类项目
    crm项目整理
    React 生成二维码
    Charles抓页面配置mac端
    Python之列表生成式、生成器、可迭代对象与迭代器
    01 Django基础
    12 jQuery的ajax
    11 事件委托(事件代理)
    10 jQuery的事件绑定和解绑
  • 原文地址:https://www.cnblogs.com/winlere/p/11427024.html
Copyright © 2020-2023  润新知