zoukankan      html  css  js  c++  java
  • 扩展莫队小总结(二) (回滚莫队/二次离线莫队)

    上一篇:扩展莫队小总结(一)

    首先分析最普通的莫队的时间复杂度:

    $Q$次询问,每次询问移动左右指针,保证移动的长度为$O(sqrt {n})$级别,每次添加或删除操作的时间都是$O(k)$

    总时间复杂度$O(Qksqrt{n})$

    回滚莫队:

    普通莫队要求删除和添加都是$O(k)$的级别,但有些情况下添加或删除操作并不能较快地修改答案,例如以下题目:

    洛谷板子题传送门

    题目大意:给你一个长度为n的序列,Q次询问,每次询问[l,r]范围内相同的数相距的最远距离

    维护两个最值桶,分别表示当前区间$[l,r]$内,数i出现的位置的最小值和最大值

    考虑普通莫队的拓展过程

    更改一个位置,先要更新桶,再更新全局答案

    添加一个位置,比如$[l,r]$变为$[l,r+1]$更新答案很容易:先用$a[r+1]$更新桶,再看是否能更新全局的答案

    删除一个位置,如$[l,r]$变为$[l,r-1]$呢?桶可以通过链表记录位置或用记录操作来更新;但全局答案可能减小!次大的答案并不能在$O(1)$时间内找到

    想个办法让莫队只有添加操作!

    我们依次枚举莫队左右指针所在块的标号$[lx,rx]$

    我们先记录$[lx+1,rx-1]$这些块的答案

    对于在$[lx,rx]$内的所有询问,每次都从$lx+1$的开头向左扩展,再从$rx-1$的末尾向右扩展,得到该询问的答案。

    再回退答案,并回退这次拓展操作对桶的贡献(这里表明回退桶的时间也必须是$O(k)$的,与添加一个位置同时间复杂度)

    接着拓展$rx$,也就是$[lx,rx]$变为$[lx,rx+1]$时,把$[lx-1,rx-1]$的答案拓展到$[lx-1,rx]$就行了

    最外层向右移动$lx$,我们直接回退所有操作,重新从lx开始记录答案

      1 #include <cmath>
      2 #include <queue>
      3 #include <cstdio>
      4 #include <cstring>
      5 #include <algorithm>
      6 #define ll long long 
      7 using namespace std;
      8 const int maxn=400000, N1=400005; const int inf=0x3f3f3f3f;
      9 
     10 template <typename _T> void read(_T &ret)
     11 {
     12     ret=0; _T fh=1; char c=getchar();
     13     while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); }
     14     while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); }
     15     ret=ret*fh; 
     16 } 
     17 
     18 int n,Q,sq=500,m,de;
     19 int tval[N1],di[N1],a[N1],b[N1];
     20 int id[N1],st[N1],ed[N1];
     21 
     22 struct QUES{
     23 int l,r,ans,id;
     24 }qu[N1];
     25 int cmp1(QUES s1,QUES s2)
     26 {
     27     if(id[s1.l]!=id[s2.l]) return id[s1.l]<id[s2.l];
     28     return id[s1.r]<id[s2.r];
     29 }
     30 int cmp2(QUES s1,QUES s2)
     31 {
     32     return s1.id<s2.id;
     33 }
     34 
     35 int ma,mcnt;
     36 struct PRE{int id,pmi,pma; }op[N1]; int cnt;
     37 int ami[N1],ama[N1];
     38 
     39 void add(int i)
     40 {
     41     cnt++; op[cnt].id=i; op[cnt].pmi=ami[b[i]], op[cnt].pma=ama[b[i]];
     42     ami[b[i]]=min(ami[b[i]],i); ama[b[i]]=max(ama[b[i]],i); 
     43     ma=max(ma,ama[b[i]]-ami[b[i]]);
     44     mcnt=max(mcnt,cnt);
     45 }
     46 void del(int i)
     47 {
     48     if(i!=op[cnt].id) puts("-1"); //if(!cnt){ puts("-1"); return; }
     49     ami[b[i]]=op[cnt].pmi, ama[b[i]]=op[cnt].pma; cnt--; 
     50     mcnt=max(mcnt,cnt);
     51 }
     52 
     53 int main()
     54 {
     55     // freopen("t.in","r",stdin);
     56     // freopen("t.txt","r",stdin);
     57     // clock_t ts,te; ts=clock();
     58     scanf("%d",&n); 
     59     for(int i=1;i<=n;i++) scanf("%d",&a[i]), tval[i]=a[i], id[i]=(i-1)/sq+1;
     60     for(int i=1;i<=id[n];i++) st[i]=(i-1)*sq+1, ed[i]=min(i*sq,n);
     61     scanf("%d",&Q);
     62     for(int q=1;q<=Q;q++)
     63     {
     64         scanf("%d%d",&qu[q].l,&qu[q].r);
     65         // read(qu[q].l), read(qu[q].r);
     66         qu[q].id=q;
     67     }
     68     sort(tval+1,tval+n+1);
     69     for(int i=1;i<=n;i++) 
     70         if(tval[i]!=tval[i-1]) di[++m]=tval[i];
     71     di[m+1]=inf;
     72     for(int i=1;i<=n;i++) b[i]=lower_bound(di+1,di+m+1,a[i])-di;
     73     for(int i=1;i<=m;i++) ami[i]=n+1, ama[i]=0;
     74     
     75     // for(int i=1;i<=n;i++) printf("%d
    ",b[i]);
     76     sort(qu+1,qu+Q+1,cmp1);
     77     int i=1,tmp,l,r; 
     78     for(int al=1;al<=id[n];al++) 
     79     {
     80         for(;i<=Q&&id[qu[i].l]==al&&id[qu[i].r]==al;i++)
     81         {
     82             ma=0;
     83             for(int j=qu[i].l;j<=qu[i].r;j++) add(j);
     84             qu[i].ans=ma;
     85             for(int j=qu[i].r;j>=qu[i].l;j--) del(j);
     86         }
     87         int nl=ed[al]+1,nr=ed[al]; ma=0;
     88         for(int ar=al+1;ar<=id[n];ar++)
     89         {
     90             for(;i<=Q&&id[qu[i].l]==al&&id[qu[i].r]==ar;i++)
     91             {
     92                 tmp=ma; 
     93                 for(l=ed[al];l>=qu[i].l;l--) add(l);
     94                 for(r=st[ar];r<=qu[i].r;r++) add(r);
     95                 qu[i].ans=ma;
     96                 for(r=qu[i].r;r>=st[ar];r--) del(r);
     97                 for(l=qu[i].l;l<=ed[al];l++) del(l);
     98                 ma=tmp;
     99             }
    100             for(;nr+1<=ed[ar];nr++) add(nr+1);
    101         }
    102         for(;nr>=nl;nr--) del(nr);
    103         if(cnt) puts("-1");
    104     }
    105     sort(qu+1,qu+Q+1,cmp2);
    106     for(int q=1;q<=Q;q++)
    107     {
    108         printf("%d
    ",qu[q].ans);
    109     }
    110     return 0;
    111 }
    View Code

    二次离线莫队:

    题目传送门:第十四分块(前体)

    题目大意:给你一个长度为n的序列a,再给定数字K,以及Q次询问, 每次询问$lle i le j le r$中,$a[i] xor a[j] $有K个1的i,j有多少对。保证$a[i]le 2^{14}$

    试试普通莫队:

    每次操作怎么搞?

    挪一下项,$a[i] xor a[j] = K个1$,变为$a[i] xor K个1 = a[j]$

    对当前区间$[l,r]$,用桶记录$a[i] xor K个1$的个数

    每次添加/删除操作先去掉这个位置的贡献,再更新桶。更新桶的最差时间复杂度是$O(C_{14}^{7})$

    总时间$O(C_{14}^{7}nsqrt {n})$

    这下啥莫队也不行了

    于是lxl神犇开发出了奇妙的二次离线莫队

    以向右拓展为例:现在我们要从$[l,r]$更新成$[l,r+x]$

    设$f(x,[l,r])$表示$x$放入$[l,r]$的桶里能得到的答案

    总贡献是$sum_{i=r+1}^{r+x}f(i,[l,i-1])=f(i,[1,i-1])-f(i,[1,l-1])$

    这个贡献是可以差分的!这是二次离线莫队能用的必要条件

    $sum_{i=r+1}^{r+x}f(i,[1,i-1])-sum_{i=r+1}^{r+x}f(i,[1,l-1])$

    前面这一项可以在$O(C_{14}^{7}n)$的时间和$O(n)$的空间预处理,莫队的过程中再$O(1)$取出

    后面这一项呢?我们把$[r+1,r+x]$这一段询问用$vector$挂在$l-1$上

    从左到右枚举$l$,更新桶,再暴力处理所有挂在$l$上的询问的答案。

    向左拓展和向右拓展的处理方式类似,删除和添加也很类似

    总时间$O(kn+nsqrt{n})$

      1 #include <cmath>
      2 #include <vector>
      3 #include <cstdio>
      4 #include <cstring>
      5 #include <algorithm>
      6 #define ll long long 
      7 using namespace std;
      8 const int maxn=2e5, N1=maxn+5;
      9 
     10 template <typename _T> void read(_T &ret)
     11 {
     12     ret=0; _T fh=1; char c=getchar();
     13     while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); }
     14     while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); }
     15     ret=ret*fh;
     16 }
     17 
     18 int n,Q,K,sq,m;
     19 int a[N1],bin[N1],popc[N1],id[N1];
     20 ll delta[N1],cnt[N1];
     21 
     22 struct QUES{ int l,r,id; ll ans; }qu[N1];
     23 int cmp1(QUES s1,QUES s2)
     24 {
     25     if(id[s1.l]!=id[s2.l]) return id[s1.l]<id[s2.l];
     26     return id[s1.r]<id[s2.r];
     27 }
     28 int cmp2(QUES s1,QUES s2){ return s1.id<s2.id; }
     29 vector<QUES>lm[N1],rm[N1];
     30 ll ld[N1],rd[N1];
     31 
     32 void getmove()
     33 {
     34     int nl=1,nr=1; ll tans; QUES t;
     35     for(int q=1;q<=Q;q++)
     36     {
     37         if(nr<qu[q].r){ lm[nl-1].push_back((QUES){nr+1,qu[q].r,q,1});  nr=qu[q].r; }
     38         if(nl>qu[q].l){ rm[nr+1].push_back((QUES){qu[q].l,nl-1,q,1});  nl=qu[q].l; }
     39         if(nl<qu[q].l){ rm[nr+1].push_back((QUES){nl,qu[q].l-1,q,-1});  nl=qu[q].l; }
     40         if(nr>qu[q].r){ lm[nl-1].push_back((QUES){qu[q].r+1,nr,q,-1});  nr=qu[q].r; }
     41     }
     42     for(int l=1;l<=n;l++)
     43     {
     44         ld[l]+=cnt[a[l]]; //前一项
     45         for(int i=1;i<=m;i++) cnt[a[l]^bin[i]]++;
     46         for(int j=0;j<lm[l].size();j++)
     47         {
     48             t=lm[l][j]; tans=0;
     49             for(int i=t.l;i<=t.r;i++) tans+=cnt[a[i]];
     50             delta[lm[l][j].id]+= tans*lm[l][j].ans*(-1);
     51         }
     52     }
     53     memset(cnt,0,sizeof(cnt));
     54     for(int r=n;r>=1;r--)
     55     {
     56         rd[r]+=cnt[a[r]]; //前一项
     57         for(int i=1;i<=m;i++) cnt[a[r]^bin[i]]++; 
     58         for(int j=0;j<rm[r].size();j++)
     59         {
     60             t=rm[r][j]; tans=0;
     61             for(int i=t.l;i<=t.r;i++) tans+=cnt[a[i]];
     62             delta[rm[r][j].id]+= tans*rm[r][j].ans*(-1);
     63         }
     64     }
     65 }
     66 void solve()
     67 {
     68     int nl=1,nr=1; ll ans=0; QUES t;
     69     for(int q=1;q<=Q;q++)
     70     {
     71         ans+=delta[q];
     72         if(nr<qu[q].r) for(;nr+1<=qu[q].r;nr++) ans+=ld[nr+1];
     73         if(nl>qu[q].l) for(;nl-1>=qu[q].l;nl--) ans+=rd[nl-1];
     74         if(nl<qu[q].l) for(;nl+1<=qu[q].l;nl++) ans-=rd[nl];
     75         if(nr>qu[q].r) for(;nr-1>=qu[q].r;nr--) ans-=ld[nr];
     76         qu[q].ans=ans;
     77     }
     78 }
     79 
     80 int main()
     81 {
     82     freopen("a.in","r",stdin);
     83     scanf("%d%d%d",&n,&Q,&K); sq=sqrt(n);
     84     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
     85     for(int i=1;i<=n;i++) id[i]=(i-1)/sq+1;
     86     for(int i=0;i<(1<<14);i++) 
     87     {
     88         popc[i]=popc[i>>1]+(i&1);
     89         if(popc[i]==K) bin[++m]=i;
     90     }
     91     for(int q=1;q<=Q;q++)
     92     {
     93         read(qu[q].l); read(qu[q].r);
     94         qu[q].id=q;
     95     }
     96     sort(qu+1,qu+Q+1,cmp1);
     97     getmove();
     98     solve();
     99     sort(qu+1,qu+Q+1,cmp2);
    100     for(int q=1;q<=Q;q++) printf("%lld
    ",qu[q].ans);
    101     return 0;
    102 }
    103         
    View Code
  • 相关阅读:
    大数据学习13_MapReduce计数器&排序和序列化
    大数据学习12_MapReduce分区
    大数据学习11_MapReduce案例实战(单词统计)
    大数据学习10_Mapreduce
    大数据学习09_HDFS3
    周总结8
    《河北省重大技术需求征集系统》10_导入大量数据做测试
    《河北省重大技术需求征集系统》08_需求浏览
    《河北省重大技术需求征集系统》07_综合查询
    《河北省重大技术需求征集系统》06_网络审核
  • 原文地址:https://www.cnblogs.com/guapisolo/p/14752500.html
Copyright © 2011-2022 走看看