二次离线

前置技能

  • 莫队
  • 修改查询 \(O(\sqrt n )-O(1)\) 平衡

概念

  • 考虑朴素莫队离线询问,过程中维护信息从 \([l,r]\) 扩展为 \([l\pm 1,r\pm 1]\) ,本质上就是要询问共 \(O(n\sqrt m)\) 次形如第 \(r\) 个元素与区间 \([l,r-1]\) 产生的贡献。
  • 当然,如果这个贡献可以差分为 \([1,r-1]\) 与 \(r\) 的贡献和 \([1,l-1]\) 与 \(r\) 的贡献,那么就可以尝试使用二次离线了。
  • 具体的,我们发现形如 \([1,r-1]\) 与 \(r\) 的贡献只有 \(n\) 个,可以直接尝试 \(O(n\sqrt n)\) 或 \(O(\ poly(n)\ \log n)\) 等复杂度处理,如果嫌麻烦,也可以放到下一段类似的处理。
  • 然后对于 \(O(n\sqrt m)\) 次询问形如 \([1,k]\) 与 \(r\) 的贡献,我们考虑扫描线,每次从 \([1,k]\) 扩展为 \([1,k+1]\) ,这样一共为 \(n\) 次修改,而在过程中我们要进行 \(O(n\sqrt m)\) 询问。而我们可以调整我们维护数据结构的方式,使得每次扩展的复杂度为 \(O( \sqrt m)\),而询问的复杂度为 \(O(1)\),从而达到平衡复杂度到 \(O(n\sqrt m)\) 级别。

优势

  • 莫队时从 \([l,r]\) 扩展为 \([l\pm 1,r\pm 1]\) 有的时候并不能做到 \(O(1)\),这是因为扩展一次需要查询一次、修改一次,这样修改和查询的次数都为 \(O(n\sqrt m)\), 本身就不太好平衡。
  • 二次离线可以通过差分将莫队扩展区间变成总共 \(O(n)\) 次修改,\(O(n\sqrt m)\) 次查询,就可以平衡了。

扩展

  • 在时间复杂度为 \(O(n \sqrt m)\) 的情况下,其空间复杂度也可以 \(O(n+m)\) 。
  • 首先要求 \([1,r-1]\) 与 \(r\) 的贡献和 \([1,l-1]\) 与 \(r\) 的贡献中 \([1,r-1]\) 与 \(r\) 必须预处理,然后所有 \([1,l-1]\) 与 \(r\) 的贡献对应到每次莫队区间扩展都对应着固定 \(l\),\(r\) 是一个连续的区间,变成 \(O(m)\) 个区间,可以线性空间存储,扫描线时每次 \(O(区间长度)\) 询问即可,总共会询问 \(O(n\sqrt m)\) 次,空间线性。

例题

  • 洛谷P5047 Yuno loves sqrt technology II
  • 离线求区间逆序对,空间 \(32MB\)
  • 具体题解就不写了,具体可以参考代码。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define debug(x) cerr<<#x<<" = "<<x
#define sp <<" "
#define el <<endl
#define fgx cerr<<" -------------------------------------------------------- "<<endl
#define LL long long
#define DB double
#define LDB long double
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
inline int read(){
int nm=0; bool fh=true; char cw=getchar();
for(;!isdigit(cw);cw=getchar()) fh^=(cw=='-');
for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
return fh?nm:-nm;
}
#define M 100020
#define B 330
int n,m,c[M],H[M],be[M],L[620],R[620],tot,tt1,tt2,d1,d2;
LL pr[M],sf[M],S[M],ans[M],cur,A[M],W[620]; pii T[M];
inline void add(int k,int dt){for(;k<=n;k+=(k&-k))c[k]+=dt;}
inline int qry(int k,int res=0){for(;k;k-=(k&-k))res+=c[k];return res;}
struct Q{
int id,ls,rs;
inline void gtin(int ID){id=ID,ls=read(),rs=read();}
inline bool operator <(const Q&ot)const{
if(be[ls]!=be[ot.ls]) return be[ls]<be[ot.ls];
if(be[ls]&1) return rs<ot.rs; return rs>ot.rs;
}
}q[M];
struct _Q{
int kt,ps,to,ls,rs; _Q(){}
_Q(int _kt,int _ps,int _to,int _ls,int _rs){kt=_kt,ps=_ps,to=_to,ls=_ls,rs=_rs;}
}pre[M],suf[M];
bool cmp_pre(_Q a,_Q b){return a.ps<b.ps;}
bool cmp_suf(_Q a,_Q b){return a.ps>b.ps;}
inline void ins(int x){for(int k=be[x];k<=tot;k++)++W[k];for(int k=x,TP=R[be[x]];k<=TP;++k)++A[k];}
inline int calc(int x){return W[be[x]-1]+A[x];}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++) H[i]=read(),T[i]=mp(H[i],i); sort(T+1,T+n+1);
for(int i=1;i<=n;i++) H[T[i].second]=i; LL Now=0;
for(L[tot=1]=1,R[tot]=B;R[tot]<n;++tot,L[tot]=R[tot-1]+1,R[tot]=R[tot-1]+B); R[tot]=n;
for(int i=1;i<=n;i++) be[i]=(i-1)/B+1;
for(int i=1;i<=n;i++) Now+=i-1-qry(H[i]),add(H[i],1),pr[i]=Now;
for(int i=1;i<=n;i++) add(H[i],-1),sf[i]=Now,Now-=qry(H[i]);
for(int i=1;i<=m;i++) q[i].gtin(i); sort(q+1,q+m+1);
for(int l=1,r=1,i=1;i<=m;i++){
if(r<q[i].rs) S[i]+=pr[q[i].rs]-pr[r],pre[++tt1]=_Q(-1,l-1,i,r+1,q[i].rs),r=q[i].rs;
if(l>q[i].ls) S[i]+=sf[q[i].ls]-sf[l],suf[++tt2]=_Q(-1,r+1,i,q[i].ls,l-1),l=q[i].ls;
if(r>q[i].rs) S[i]-=pr[r]-pr[q[i].rs],pre[++tt1]=_Q(1,l-1,i,q[i].rs+1,r),r=q[i].rs;
if(l<q[i].ls) S[i]-=sf[l]-sf[q[i].ls],suf[++tt2]=_Q(1,r+1,i,l,q[i].ls-1),l=q[i].ls;
} sort(pre+1,pre+tt1+1,cmp_pre),sort(suf+1,suf+tt2+1,cmp_suf),d1=d2=1;
while(d1<=tt1&&(!pre[d1].ps)) ++d1; while(d2<=tt2&&suf[d2].ps>n) ++d2;
for(int i=1;i<=n&&d1<=tt1;i++)
for(ins(H[i]);d1<=tt1&&pre[d1].ps==i;S[pre[d1].to]+=(LL)pre[d1].kt*(LL)(pre[d1].rs-pre[d1].ls+1)*(LL)i,++d1)
for(int k=pre[d1].ls;k<=pre[d1].rs;++k) S[pre[d1].to]-=pre[d1].kt*calc(H[k]);
memset(A,0,sizeof(A)),memset(W,0,sizeof(W));
for(int i=n;i>0&&d2<=tt2;--i) for(ins(H[i]);d2<=tt2&&suf[d2].ps==i;++d2)
for(int k=suf[d2].ls;k<=suf[d2].rs;++k) S[suf[d2].to]+=suf[d2].kt*calc(H[k]);
for(int i=1;i<=m;i++) cur+=S[i],ans[q[i].id]=cur;
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]); return 0;
}

有关二次离线和 Yuno loves sqrt technology II的更多相关文章

  1. 题解 Yuno loves sqrt technology II

    题目传送门 题目大意 有\(n\)个数,\(m\)个查询,每次查询一个区间内的逆序对个数. \(n,m\le 10^5\) 思路 其实是为了锻炼二次离线才做这道题的. 不难想到可以有一个\(\Thet ...

  2. [Ynoi2019模拟赛]Yuno loves sqrt technology II(二次离线莫队)

    二次离线莫队. 终于懂了 \(lxl\) 大爷发明的二次离线莫队,\(\%\%\%lxl\) 二次离线莫队,顾名思义就是将莫队离线两次.那怎么离线两次呢? 每当我们将 \([l,r]\) 移动右端点到 ...

  3. [Ynoi2019模拟赛]Yuno loves sqrt technology II

    题目大意: 给定一个长为\(n\)的序列,\(m\)次询问,每次查询一个区间的逆序对数. 32MB. 解题思路: 出题人题解 众所周知lxl是个毒瘤,Ynoi道道都是神仙题 二次离线莫队. 对于每个区 ...

  4. 洛谷 P5048 - [Ynoi2019 模拟赛] Yuno loves sqrt technology III(分块)

    题面传送门 qwq 感觉跟很多年前做过的一道题思路差不多罢,结果我竟然没想起那道题?!!所以说我 wtcl/wq 首先将 \(a_i\) 离散化. 如果允许离线那显然一遍莫队就能解决,复杂度 \(n\ ...

  5. [Luogu5048] [Ynoi2019模拟赛]Yuno loves sqrt technology III[分块]

    题意 长为 \(n\) 的序列,询问区间众数,强制在线. \(n\leq 5\times 10^5\). 分析 考虑分块,暴力统计出整块到整块之间的众数次数. 然后答案还可能出现在两边的两个独立的块中 ...

  6. [luogu5048] [Ynoi2019模拟赛] Yuno loves sqrt technology III

    题目链接 洛谷. Solution 思路同[BZOJ2724] [Violet 6]蒲公英,只不过由于lxl过于毒瘤,我们有一些更巧妙的操作. 首先还是预处理\(f[l][r]\)表示\(l\sim ...

  7. [洛谷P5048][Ynoi2019模拟赛]Yuno loves sqrt technology III

    题目大意:有$n(n\leqslant5\times10^5)$个数,$m(m\leqslant5\times10^5)$个询问,每个询问问区间$[l,r]$中众数的出现次数 题解:分块,设块大小为$ ...

  8. 洛谷P5048 [Ynoi2019模拟赛]Yuno loves sqrt technology III(分块)

    传送门 众所周知lxl是个毒瘤,Ynoi道道都是神仙题 用蒲公英那个分块的方法做结果两天没卡过去→_→ 首先我们分块,预处理块与块之间的答案,然后每次询问的时候拆成整块和两边剩下的元素 整块的答案很简 ...

  9. Luogu P5048 [Ynoi2019模拟赛]Yuno loves sqrt technology III 分块

    这才是真正的$N\sqrt{N}$吧$qwq$ 记录每个数$vl$出现的位置$s[vl]$,和每个数$a[i]=vl$是第几个$vl$,记为$P[i]$,然后预处理出块$[i,j]$区间的答案$f[i ...

随机推荐

  1. Nginx如何配置反向代理

    server { listen 80; server_name 代理域名; location / { proxy_pass 应用域名:应用端口; proxy_set_header Host $host ...

  2. appium 自动化测试框架详读(一)

    appium框架使用的过程记录,开始使用markdown来语法来编写,不知道博客园是否会支持markdown语法 ***1. appium原理* appium启动时,创建一个http://127.0. ...

  3. python-webdriver中添加cookie,解决添加了图片验证码的问题

    遇到问题:之前一直能用的脚本突然跑不通了,仔细一看原来是研发新加了图片验证码...... 解决问题: 手动抓取了cookie并塞进去,解决问题.当然如果你的cookie有效期太短或者是随着会话关闭就失 ...

  4. linux使用VNC服务轻松远程安装oracle

    VNC服务在远程服务器上安装oracle,新手安装oracle时总会遇到这样或者那样的问题,下面我就详细解说一下安装过程,其实oracle安装很简单,并不要把他相像的特别复杂. 本环境用:centos ...

  5. concurrent.futures:线程池,让你更加高效、并发的处理任务

    并发任务池 concurrent.futures模块提供了使用工作线程或进程池运行任务的接口. 线程池和进程池的API是一致的,所以应用只需要做最小的修改就可以在线程和进程之间进行切换 这个模块提供了 ...

  6. opengl学习-利用模板测试勾画物体轮廓中出现的一个问题

    我在学习OpenGL模板测试勾画物体轮廓的时候,出现了这个问题: 这个出现的原因就是,改变摄像机的时候,每次绘制,上次绘制中模板缓冲区的数据没有清除的原因.也就是在while循环开始的时候,glCle ...

  7. Zookeeper包中,slf4j-log4j12和log4j冲突问题解决

    程序启动时会有日志警告 SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/E ...

  8. php禁止个别ip访问网站

    PHP禁止个别IP访问自己的网站,可以看看下面的方法. function get_ip_data(){ $ip=file_get_contents("http://ip.taobao.com ...

  9. java8学习之Collector复合与注意事项

    接着上一次[http://www.cnblogs.com/webor2006/p/8318066.html]继续对Collector进行javadoc详读,上一次读到了这: 接下来一条条来过目一下: ...

  10. wx小程序知识点(二)

    二.WXML和HTML的异同.WXSS和CSS的异同 (1)WXML和HTML 相同点:都是用来描述页面结构的,由标签.属性组成 不同点:标签名不一样,小程序标签名更少: 小程序多了 wx:if 这样 ...