Solution -「NOI 2020」时代的眼泪
Description
Link。
给出一个二维平面以及一些点,保证点不在同行 / 同列。每次询问求出一个子矩阵里面的顺序对。
Solution
卡常,卡你吗。
膜拜 dX。
基本是把 dX 的题解贺了过来所以没啥参考的价值。
不过有很多细节的处理不一样,大概能算个 \(\frac{1}{50}\) 成新?
对序列分块,把贡献分成 整块 - 散块 / 整块 - 整块/ 散块 - 整块 / 散块 - 散块 以及 散块内部 / 整块内部 共六种贡献。
记 \(\textit{ans}_{0}(l,r,x,y)\) 为询问 \(l,r,x,y\) 的答案。
同时预处理出 \(\textit{lb}(i,j),\textit{ub}(i,j)\) 分别表示在块 \(i\) 中数 \(j\) 的 std::lower_bound / std::upper_bound 值,下文如果写成单元函数的形式那么就是省去了第一维。
以及预先做一个块内排序,记为 \(\textit{ord}(i,j)\),表示块 \(i\) 中排序后的第 \(j\) 个元素。
注意本文在每个 subtask 定义的东西在其他 subtask 依然适用。
- 散块 - 散块;
两边的都是 \(\sqrt{n}\) 级别,拉出来分别排序后归并计算顺序对即可。
- 散块内部
考虑如何对 \(\textit{ans}_{0}(l,r,x,y)\) 进行容斥。
主要矛盾集中在:会出现 \((a\in[1,x),b\in[x,y])\) 这样的贡献。令 \(\textit{cnt}_{0}(i,j)\) 表示 \([\textit{lp},i]\) 中 \(\textit{rank}_{1}\) 小于 \(j\) 的数的数量,其中 \(\textit{lp}\) 是当前块的左端点,下同,如果出现 \(\textit{rp}\) 同理,\(\textit{rank}_{1}\) 的定义见下文。
则容斥可以写为 \(\textit{ans}_{0}(l,r,x,y)=\textit{ans}_{0}(l,r,1,y)-\textit{ans}_{0}(l,r,1,x-1)-\sum_{i=l}^{r}[a_{i}\in[x,y]]\cdot\textit{cnt}_{0}(i,\textit{lb}(x)-1)\)。
又有 \(\textit{ans}_{0}(l,r,1,x)=\sum_{i=l}^{r}[a_{i}\leqslant x]\cdot\textit{cnt}_{0}(i,\textit{rank}_{1}(i))\),我们就可以做到单次 \(\mathcal{O}(\sqrt{n})\),注意的 \(l,r\) 触及散块边界者不同时,对 \(\textit{cnt}_{0}\) 的容斥也有区别。
- 整块 - 整块
令 \(\textit{cnt}_{1}(i,j)\) 为块 \([1,i]\) 中 \(\leqslant j\) 的元素个数,\(\textit{ans}_{1}(L,R,x,y)\) 为块 \([L,R]\) 的答案,以及 \(\textit{rank}_{0}(i,j)\) 是块 \(i\) 中排名 \(j\) 的元素在原数组中的下标。
我们掏出传统容斥:\(\textit{ans}_{1}(L,R,x,y)=\textit{ans}_{1}(L,R,1,y)-\textit{ans}_{1}(L,R,1,x-1)-\sum_{i=L}^{R}P_{i}Q_{i}\),\(P_{i}\) 是块 \([L,i)\) 中 \(<x\) 的元素个数,\(Q_{i}\) 是块 \(i\) 种 \(\in[x,y]\) 的元素个数。
考虑算 \(\textit{ans}_{1}(L,R,1,x)\)。
定义 \(\textit{rank}_{1}(i,j)\) 为块 \(i\) 中第 \(j\) 个元素的排名(从小到大,下同),\(\textit{rank}_{2}(i,j)\) 为块 \(i\) 中满足 \(<j\) 的最大元素的排名,\(\textit{pre}_{b}(i,j)\) 为块 \([i,j]\) 中所有 \(<\textit{rank}_{1}(i,j)\) 的元素数量。
易知 \(\textit{pre}_{b}(i,j)=\textit{cnt}_{1}(i,\textit{rank}_{1}(i,j)-1)\),再定义 \(\overset{\sqrt{n},\sqrt{n},\sqrt{n}}{\textit{cp}_{0}(i,L,r)}\) 为块 \([1,L]\) 与块 \(i\) 前 \(r\) 小的元素组成的顺序对数量,同样易知 \(\textit{cp}_{0}(i,L,r)=\sum_{k\in T}[\textit{rank}_{1}(i,k)\leqslant r]\cdot\textit{pre}_{b}(L,\textit{rank}_{1}(i,k))\),其中 \(T\) 是块 \(i\) 的元素集。但这样搞状态数 \(\mathcal{O}(n\sqrt{n})\) 转移还要 \(\mathcal{\sqrt{n}}\) 而且不好前缀和。
不过可以发现使用 \(\textit{ord}\) 数组 \(\textit{cp}_{0}\) 就可以递推了:\(\textit{cp}_{0}(i,L,r)=\sum_{k=lp}^{r+lp-1}\textit{pre}_{b}(L,k)=\textit{cp}_{0}(i,L,r-1)+\textit{pre}_{b}(L,r+lp-1)\)。
然后 \(\textit{ans}_{1}(L,R,1,x)=\sum_{i=L+1}^{R}\textit{cp}_{0}(i,i-1,\textit{rank}_{2}(i,x))-\textit{cp}_{0}(i,L-1,\textit{rank}_{2}(i,x))\)。
预处理 \(\textit{cp}_{0}\) 是 \(\mathcal{O}(n\sqrt{n})\),单次回答 \(\mathcal{O}(\sqrt{n})\)。
- 散块 - 整块
枚举散块里面的元素,利用 \(\textit{cnt}_{1}(i,j)\) 计算答案。
具体是令散块元素集为 \(T\),整块编号为 \(L,R\), \(\sum_{i\in T}\textit{cnt}_{1}(R,i)-\textit{cnt}_{1}(L-1,i)\)。
- 整块 - 散块
和上面有什么区别吗?
- 整块内部
预处理数组 \(\overset{\sqrt{n},\sqrt{n},\sqrt{n}}{\textit{cp}_{1}(i,x,y)}\) 表示取 \(\textit{ord}(i,x\dots y)\) 组成的序列的顺序对数量。
用 \(\textit{rank}_{0}\) 来预处理:\(\textit{cp}_{1}(i,x,y)=\textit{cp}_{1}(i,x,y-1)+\textit{cnt}_{0}(\textit{rank}_{0}(i,y),y-1)-\textit{cnt}_{0}(\textit{rank}_{0}(i,y),x-1)\)。
综上,这个问题得以一个 \(\mathcal{O}(n\sqrt{n})\) 的在线算法解决。
代码也是抄的 dX,像个 shit 一样就折叠了。
% 死X
//almost copied from dead_X sry
//kouhu has no qiantu
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
inline int Read()
{
int x=0;char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9') x=x*10+(c&15),c=getchar();
return x;
}
const int N=101111,A=460,BS=A+10;
ll cp0[BS][BS][BS];
int a[N],rk0[BS][BS],cnt0[BS][N],cp1[BS][BS][BS],lb[BS][N],rk1[N],cnt1[BS][N],L[BS],R[BS];
bool cmp(int x,int y) { return a[x]<a[y]; }
namespace IO{
const int sz=1<<22;
char a[sz+5],b[sz+5],*p1=a,*p2=a,*t=b,p[105];
inline char gc(){
return p1==p2?(p2=(p1=a)+fread(a,1,sz,stdin),p1==p2?EOF:*p1++):*p1++;
}
template<class T> void gi(T& x){
x=0; char c=gc();
for(;c<'0'||c>'9';c=gc());
for(;c>='0'&&c<='9';c=gc())
x=(x<<3)+(x<<1)+(c-'0');
}
inline void flush(){fwrite(b,1,t-b,stdout),t=b; }
inline void pc(char x){*t++=x; if(t-b==sz) flush(); }
template<class T> void pi(T x,char c='\n'){
if(x<0) x=-x;
if(x==0) pc('0'); int t=0;
for(;x;x/=10) p[++t]=x%10+'0';
for(;t;--t) pc(p[t]); pc(c);
}
struct F{~F(){flush();}}f;
}
using IO::gi;
using IO::pi;
inline int read() { int r; gi(r); return r; }
int main(){
#ifdef ONLINE_JUDGE
freopen("tears.in","r",stdin);
freopen("tears.out","w",stdout);
#endif
int n=read(),m=read(),B=n/A;
for(int i=0;i<n;++i)a[i]=read();
for(int i=n;i<(B+1)*A;++i)a[i]=i;
for(int i=0;i<=B;++i){
for(int j=i*A,k=0;k<A;++j,++k)rk0[i][k]=j;
sort(rk0[i],rk0[i]+A,[](int x,int y){return a[x]<a[y];});
for(int j=0;j<A;++j)rk1[rk0[i][j]]=j,cnt0[j][rk0[i][j]]=1;
for(int j=i*A+1;j<(i+1)*A;++j)
for(int k=0;k<A;++k)cnt0[k][j]+=cnt0[k][j-1];
for(int j=i*A;j<(i+1)*A;++j)
for(int k=1;k<A;++k)cnt0[k][j]+=cnt0[k-1][j];
for(int j=i*A;j<(i+1)*A;++j)++cnt1[i][a[j]];
if(i)for(int j=1;j<=101000;++j)cnt1[i][j]+=cnt1[i-1][j];
for(int j=1,k=0;j<=101000;++j)(k<A)&&(j>=a[rk0[i][k]])&&(++k),lb[i][j]=k;
}
for(int i=0;i<=B;++i)
for(int j=1;j<=101000;++j)cnt1[i][j]+=cnt1[i][j-1];
for(int i=1;i<B;++i)for(int j=0;j<i;++j)for(int k=0;k<A;++k)
cp0[i][j][k+1]=cnt1[j][a[rk0[i][k]]]+cp0[i][j][k];
for(int i=0;i<B;++i)for(int j=0;j<A;++j)for(int k=j+1;k<A;++k)
cp1[i][j][k]=cp1[i][j][k-1]+cnt0[k-1][rk0[i][k]]-((j==0)?0:cnt0[j-1][rk0[i][k]]);
for(;m;--m){
int l=read()-1,r=read()-1,x=read(),y=read(),bl=l/A,br=r/A;
if(bl==br){
int ans=0;
for(int i=l;i<=r;++i){
if(x<=a[i]&&a[i]<=y&&rk1[i])ans+=cnt0[rk1[i]-1][i]-((l%A)?cnt0[rk1[i]-1][l-1]:0);
if(lb[bl][x-1]&&x<=a[i]&&a[i]<=y)ans-=cnt0[lb[bl][x-1]-1][i]-((l%A&&lb[bl][x-1])?cnt0[lb[bl][x-1]-1][l-1]:0);
}
pi(ans);
}
else{
ll ans=0;
for(int i=l;i<(bl+1)*A;++i){
if(x<=a[i]&&a[i]<=y&&rk1[i])ans+=cnt0[rk1[i]-1][i]-((l%A)?cnt0[rk1[i]-1][l-1]:0);
if(lb[bl][x-1]&&x<=a[i]&&a[i]<=y)ans-=cnt0[lb[bl][x-1]-1][i]-((l%A&&lb[bl][x-1])?cnt0[lb[bl][x-1]-1][l-1]:0);
if(x<=a[i]&&a[i]<=y)ans+=cnt1[br-1][y]-cnt1[bl][y]-cnt1[br-1][a[i]]+cnt1[bl][a[i]];
}
for(int i=br*A;i<=r;++i){
if(x<=a[i]&&a[i]<=y&&rk1[i])ans+=cnt0[rk1[i]-1][i];
if(lb[br][x-1]&&x<=a[i]&&a[i]<=y)ans-=cnt0[lb[br][x-1]-1][i];
if(x<=a[i]&&a[i]<=y)ans+=cnt1[br-1][a[i]]-cnt1[bl][a[i]]-cnt1[br-1][x-1]+cnt1[bl][x-1];
}
int lt=0,rt=0;
for(int i=0;i<A;++i){
if(rk0[bl][i]>=l&&x<=a[rk0[bl][i]]&&a[rk0[bl][i]]<=y)L[++lt]=rk0[bl][i];
if(rk0[br][i]<=r&&x<=a[rk0[br][i]]&&a[rk0[br][i]]<=y)R[++rt]=rk0[br][i];
}
for(int i=1,t=1;i<=rt;++i){
while(t<=lt&&a[L[t]]<a[R[i]])++t;
ans+=t-1;
}
for(int i=bl+1;i<br;++i)if(lb[i][y])ans+=cp1[i][lb[i][x-1]][lb[i][y]-1];
for(int i=bl+2;i<br;++i)
ans+=cp0[i][i-1][lb[i][y]]-cp0[i][bl][lb[i][y]]-cp0[i][i-1][lb[i][x-1]]+cp0[i][bl][lb[i][x-1]],
ans-=ll(cnt1[i][y]-cnt1[i-1][y]-cnt1[i][x-1]+cnt1[i-1][x-1])*(cnt1[i-1][x-1]-cnt1[bl][x-1]);
pi(ans);
}
}
return 0;
}
Solution -「NOI 2020」时代的眼泪的更多相关文章
- Solution -「NOI 2020」「洛谷 P6776」超现实树
\(\mathcal{Description}\) Link. 对于非空二叉树 \(T\),定义 \(\operatorname{grow}(T)\) 为所有能通过若干次"替换 \( ...
- Solution -「NOI 2021」「洛谷 P7740」机器人游戏
\(\mathcal{Description}\) Link. 自己去读题面叭~ \(\mathcal{Solution}\) 首先,参悟[样例解释 #2].一种暴力的思路即为钦定集合 \ ...
- Solution -「ZJOI 2020」「洛谷 P6631」序列
\(\mathcal{Description}\) Link. 给定一个长为 \(n\) 的非负整数序列 \(\lang a_n\rang\),你可以进行如下操作: 取 \([l,r]\),将 ...
- Solution -「JOISC 2020」「UOJ #509」迷路的猫
\(\mathcal{Decription}\) Link. 这是一道通信题. 给定一个 \(n\) 个点 \(m\) 条边的连通无向图与两个限制 \(A,B\). 程序 Anthon ...
- Solution -「NOI 2016」「洛谷 P1587」循环之美
\(\mathcal{Description}\) Link. 给定 \(n,m,k\),求 \(x\in [1,n]\cap\mathbb N,y\in [1,m]\cap \mathbb ...
- Solution -「FJWC 2020」人生
\(\mathcal{Description}\) OurOJ. 有 \(n\) 个结点,一些结点有染有黑色或白色,其余待染色.将 \(n\) 个结点染上颜色并连接有向边,求有多少个不同(结点 ...
- Solution -「NOI 2012」「洛谷 P2050」美食节
\(\mathcal{Description}\) Link. 美食节提供 \(n\) 种菜品,第 \(i\) 种的需求量是 \(p_i\),菜品由 \(m\) 个厨师负责制作,第 \(j\) ...
- Solution -「NOI 2008」「洛谷 P3980」志愿者招募
\(\mathcal{Description}\) Link. 一项持续 \(n\) 天的任务,第 \(i\) 天需要至少 \(a_i\) 人工作.还有 \(m\) 种雇佣方式,第 \(i\) ...
- Solution -「NOI 2018」「洛谷 P4768」归程
\(\mathcal{Description}\) Link. 给定一个 \(n\) 个点 \(m\) 条边的无向连通图,边形如 \((u,v,l,a)\).每次询问给出 \(u,p\),回答 ...
- 「NOIP 2020」微信步数(计数)
「NOIP 2020」微信步数(Luogu P7116) 题意: 有一个 \(k\) 维场地,第 \(i\) 维宽为 \(w_i\),即第 \(i\) 维的合法坐标为 \(1, 2, \cdots, ...
随机推荐
- WinUI(WASDK)使用HelixToolkit加载3D模型并进行项目实践
前言 本人之前开发了一个叫电子脑壳的上位机应用,给稚晖君ElectronBot开源机器人提供一些功能,但是由于是结合硬件才能使用的软件,如果拥有硬件的人员太少,就会导致我的软件没什么人用,于是我就想着 ...
- 沉思篇-剖析JetPack的Lifecycle
这几年,对于Android开发者来说,最时髦的技术当属Jetpack了.谷歌官方从19年开始,就在极力推动Jetpack的使用,经过这几年的发展,Jetpack也基本完成了当时的设计目标--简单,一致 ...
- 【LeetCode】Find Pivot Index #724 Rust Solution
给定一个整数类型的数组 nums,请编写一个能够返回数组 "中心索引" 的方法.我们是这样定义数组 中心索引 的:数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和.如果数 ...
- SpringBoot集成支付宝 - 少走弯路就看这篇
最近在做一个网站,后端采用了SpringBoot,需要集成支付宝进行线上支付,在这个过程中研究了大量支付宝的集成资料,也走了一些弯路,现在总结出来,相信你读完也能轻松集成支付宝支付. 在开始集成支付宝 ...
- 基于C语言的泛类型循环队列
循环队列多用于通信数据缓存中,尤其是在双方设备接收数据与处理数据不同步的情况下,使用循环队列先缓存通信数据,然后按照时间戳数据出队作出相应的处理,是一种比较合适的做法,在嵌入式编程中亦是如此.使用循环 ...
- OOP4-6题目集总结
4-6次题目集,从集合框架,正则表达式,类的继承与多态三个方面展开,在帮助我们了解java常用的工具(集合框架,正则表达式)的同时让我们学着利用类与类之间的关系来减少耦合,第六次题目集侧重于类的继承与 ...
- PlayWright(十二)- PO模式
1.PO模式是什么? PO,即Page Object,直译为页面对象,代表 Web 应用程序的一部分 具体什么意思呢,通俗来讲,一个页面有输入.点击.搜索功能,而且有很多页面,这时候我们就采用每个 ...
- 从头学Java17-Stream API(一)
Stream API Stream API 是按照map/filter/reduce方法处理内存中数据的最佳工具. 本系列中的教程包含从基本概念一直到collector设计和并行流. 在流上添加中继操 ...
- 一文学会TextureID渲染到Surface
最近遇到一个需求,要求将一个GL_TEXTURE_2D类型的纹理ID写入到ImageReader生成的Surface中. 其实这个需求与我之前写过的一篇文章 一文学会MediaCodeC与OpenGL ...
- PerfView专题 (第十三篇):洞察 .NET程序 的非托管句柄泄露
一:背景 1. 讲故事 前几天写了一篇 如何洞察 .NET程序 非托管句柄泄露 的文章,文中使用 WinDbg 的 !htrace 命令实现了句柄泄露的洞察,在文末我也说了,WinDbg 是以侵入式的 ...