CF765F Souvenirs
可以发现这个问题类似询问区间逆序对(都和排序相关,都和点对相关),于是我们可以顺着 P5046 的方法去想。
分块。把询问分成询问端点同块与询问端点异块的情况。
询问端点同块
此时可以考虑处理出每个块排序后的数组。沿着排序后的数组,按大小顺序地“取出”\(l\sim r\) 的这一段区间,然后统计。
询问端点异块
参照 P5046 分成如下几部分:
- 散块内部与散块之间
- 整块与散块之间
- 整块内部与整块之间
散块内部与散块之间
与询问端点同块情况类似,取出两个散块分别得排序后的状态,归并起来,然后统计,我直接用了 STL。
整块与散块之间
预处理两个数组:
- \(D1_{i,j}\) 代表 \(i\) 所在块以 \(i\) 结尾的前缀 与 \(i\) 所在块的下一块至第 \(j\) 块 这两个区间的答案。
- \(D2_{i,j}\) 代表 \(i\) 所在块以 \(i\) 开头的后缀 与 \(i\) 所在块的上一块至第 \(j\) 块 这两个区间的答案。
那如何预处理呢?可以考虑先处理 一个位置对一个块的答案,再前缀和变成 一个前缀/后缀对一个块的答案,再前缀和成 一个前缀/后缀对一些块的答案。
那 一个位置对一个块的答案 怎么算呢?可以先钦定两个块 \(a,b\),然后用双指针算块 \(a\) 内的所有数分别与块 \(b\) 的答案。
具体算法为:块 \(a\) 内的元素从小往大算(别忘了我们之前给每个块排过序),然后定义一个指针 \(p\),\(p\) 一开始位于 \(b\) 的块首,然后随着 \(a\) 内计算的位置从前往后,\(p\) 也随着变大,期间满足排序后的数组内 \(a_p<a_i\)。
这两个数组没有相交的地方,考虑将其合并。
整块内部与整块之间
考虑预处理 \(S_{i,j}\) 代表块 \(i\) 开头至块 \(j\) 结尾之间的答案。
可以先处理 \(S_{i,i}\) 代表一个块的答案,然后利用 \(D\) 数组扩展到多个块的答案。
代码
感觉写的还算简单,100 行不到。
#include<stdio.h>
#include<math.h>
#include<algorithm>
const int maxn = 1e5 + 5;
const int sqr = 325;
const int inf = 1e9 + 5;
int n,m,a[maxn],B,C;
int D[maxn][sqr],S[sqr][sqr];
int st[sqr],ed[sqr],bl[maxn];
int tmp[maxn];
struct value{
int i,x;
bool operator<(const value& v) const {
return x < v.x;
}
} b[maxn];
int qry0(rint l,rint r){
int ans = inf,len = 0;
rep(i,st[bl[l]],ed[bl[l]])
if(l <= b[i].i && b[i].i <= r) tmp[++len] = b[i].x;
rep(i,2,len) chkmin(ans,tmp[i] - tmp[i - 1]);
return ans;
}
int qry1(rint l1,rint r1,rint l2,rint r2){
int len = 0,p = r1 - l1 + 1,ans = inf;
rep(i,st[bl[l1]],ed[bl[l1]])
if(l1 <= b[i].i && b[i].i <= r1) tmp[++len] = b[i].x;
rep(i,st[bl[l2]],ed[bl[l2]])
if(l2 <= b[i].i && b[i].i <= r2) tmp[++len] = b[i].x;
std::inplace_merge(tmp + 1,tmp + p + 1,tmp + len + 1);
rep(i,2,len) chkmin(ans,tmp[i] - tmp[i - 1]);
return ans;
}
void init(){
B = sqrt(n); C = (n - 1) / B + 1;
rep(i,1,C){
st[i] = (i - 1) * B + 1;
ed[i] = i == C ? n : i * B;
rep(j,st[i],ed[i]) bl[j] = i,b[j].i = j,b[j].x = a[j];
std::sort(b + st[i],b + ed[i] + 1);
}
rep(i,1,C){
rep(j,1,C) if(i ^ j){
rint p = st[j];
rep(k,st[i],ed[i]){
while(p < ed[j] && b[p + 1].x < b[k].x) ++p;
D[b[k].i][j] = abs2(b[k].x - b[p].x);
if(p < ed[j]) chkmin(D[b[k].i][j],abs2(b[k].x - b[p + 1].x));
}
}
rep(j,st[i],ed[i]) rep(k,i + 2,C) chkmin(D[j][k],D[j][k - 1]);
rep(j,st[i],ed[i]) Rep(k,i - 2,0) chkmin(D[j][k],D[j][k + 1]);
rep(k,1,i - 1) rep(j,st[i] + 1,ed[i]) chkmin(D[j][k],D[j - 1][k]);
rep(k,i + 1,C) Rep(j,ed[i] - 1,st[i]) chkmin(D[j][k],D[j + 1][k]);
S[i][i] = qry0(st[i],ed[i]);
}
rep(i,1,C) rep(j,i + 1,C) S[i][j] = min2(min2(S[i][j - 1],S[j][j]),D[ed[j]][i]);
}
int qry(rint l,rint r){
if(bl[l] == bl[r]) return qry0(l,r);
int ans = qry1(l,ed[bl[l]],st[bl[r]],r);
if(bl[l] + 1 <= bl[r] - 1){
chkmin(ans,S[bl[l] + 1][bl[r] - 1]);
chkmin(ans,min2(D[r][bl[l] + 1],D[l][bl[r] - 1]));
}
return ans;
}
int main(){
read(n); rep(i,1,n) read(a[i]);
init(); read(m);
while(m--){
int l,r; read(l,r);
print(qry(l,r),'\n');
}
return flush(),0;
}

浙公网安备 33010602011771号