题解 [CF765F] Souvenirs
让我来胡一些应该能艹过的做法(
首先这题看着就很莫队
用 set 维护,插入一个数时查前驱后继就可以做到 \(O(n\sqrt m\log n)\)
然后发现只要查前驱后继,可以先离散化后用树状数组代替 set 做到小常数 \(O(n\sqrt m\log n)\)
然后发现其实是可以直接分块的
对第 \(i\) 个块预处理 \(f_{i, j}\) 为 \([第 i 个块左端点, j]\) 的答案
再弄一个 \(g_{i, j}\) 为 \([j, 第 i 个块右端点]\) 的答案
这两个东西的预处理可以用链表化加为删
艹这个 log 好像很难去,爬了爬了
- 关于序列分块维护区间查询区间内点对关系的一种常见方法:
令 \(f_{i, j}\) 为 \(i\) 所在块以 \(i\) 为结尾的一段前缀 和 \([j, i-1]\) 中的块产生的贡献
令 \(g_{i, j}\) 为 \(i\) 所在块以 \(i\) 为结尾的一段后缀 和 \([i+1, j]\) 中的块产生的贡献
令 \(h_{i, j}\) 为编号 \(\in [i, j]\) 中的块两两产生的贡献和
那么此时询问一个区间时只需要考虑求出两边的散块之间产生的贡献
然后 \(f\) 和 \(g\) 的预处理可以考虑计算每个点 \(i\) 与每个块之间的贡献(这个需要尝试将每个块内的 \(i\) 的这个东西一起算出来)
有了这个东西,再跑个前后缀就有了定义里的东西了
然后 \(h\) 也可以用 \(f\) 求出来
还能发现 \(f\) 和 \(g\) 无交,所以可以共用一个数组
对于本题,按上面的方法分块即可,见这里 mrsrz 的题解
复杂度 \(O(n\sqrt n)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m;
int a[N], bel[N], sta[N], sqr, top;
int f[N][325], h[325][325], blc[325][325], val[325][325], siz[325], usiz[325];
signed main()
{
n=read(); sqr=sqrt(n);
memset(f, 0x3f, sizeof(f));
memset(h, 0x3f, sizeof(h));
for (int i=1; i<=n; ++i) a[i]=read();
m=read();
for (int i=1; i<=n; ++i) bel[i]=(i-1)/(sqr)+1;
// cout<<"i : "; for (int i=1; i<=n; ++i) cout<<i<<' '; cout<<endl;
// cout<<"bel: "; for (int i=1; i<=n; ++i) cout<<bel[i]<<' '; cout<<endl;
for (int i=1; i<=n; ++i) blc[bel[i]][++siz[bel[i]]]=i, val[bel[i]][++usiz[bel[i]]]=a[i];
for (int i=1; i<=bel[n]; ++i) sort(blc[i]+1, blc[i]+siz[i]+1, [](int i, int j){return a[i]<a[j];});
for (int i=1; i<=bel[n]; ++i) {
val[i][0]=-INF;
val[i][++usiz[i]]=INF;
sort(val[i]+1, val[i]+usiz[i]+1);
usiz[i]=unique(val[i]+1, val[i]+usiz[i]+1)-val[i]-1;
}
for (int i=1; i<=bel[n]; ++i)
for (int j=1; j<=bel[n]; ++j) if (i!=j)
for (int p1=1,p2=1; p2<=usiz[j]; ++p2)
for (; p1<=siz[i]&&a[blc[i][p1]]<=val[j][p2]; ++p1) {
// cout<<"upd: "<<a[blc[i][p1]]<<' '<<val[j][p2]<<endl;
f[blc[i][p1]][j]=min(f[blc[i][p1]][j], abs(a[blc[i][p1]]-val[j][p2]));
if (p2) f[blc[i][p1]][j]=min(f[blc[i][p1]][j], abs(a[blc[i][p1]]-val[j][p2-1]));
// printf("f[%d][%d]=%d\n", blc[i][p1], j, f[blc[i][p1]][j]);
}
for (int i=1; i<=n; ++i) {
for (int j=bel[i]-1; j; --j) f[i][j]=min(f[i][j], f[i][j+1]);
for (int j=bel[i]+1; j<=bel[n]; ++j) f[i][j]=min(f[i][j], f[i][j-1]);
}
for (int i=1; i<=n; ++i) if (bel[i-1]==bel[i])
for (int j=1; j<bel[i]; ++j) f[i][j]=min(f[i][j], f[i-1][j]);
for (int i=n; i; --i) if (bel[i+1]==bel[i])
for (int j=bel[i]+1; j<=bel[n]; ++j) f[i][j]=min(f[i][j], f[i+1][j]);
for (int i=1; i<=n; ++i) if (bel[i]!=bel[i-1]) {
for (int j=1,lst=-INF; j<=siz[bel[i]]; ++j)
h[bel[i]][bel[i]]=min(h[bel[i]][bel[i]], a[blc[bel[i]][j]]-lst), lst=a[blc[bel[i]][j]];
for (int j=bel[i]+1; j<=bel[n]; ++j)
h[bel[i]][j]=min(h[bel[i]][j], min(h[bel[i]][j-1], f[i][j]));
}
for (int i=1; i<=n; ++i) if (bel[i]!=bel[i+1])
for (int j=bel[i]-1; j; --j)
h[j][bel[i]]=min(h[j][bel[i]], min(h[j+1][bel[i]], f[i][j]));
for (int i=1,l,r,ans; i<=m; ++i) {
l=read(); r=read(); ans=INF;
int sid=bel[l], eid=bel[r];
if (sid==eid) {
int lst=-INF;
for (int j=1; j<=siz[sid]; ++j) if (l<=blc[sid][j]&&blc[sid][j]<=r)
ans=min(ans, a[blc[sid][j]]-lst), lst=a[blc[sid][j]];
}
else {
int p1=1, p2=1; top=0;
while (p1<=siz[sid]||p2<=siz[eid]) {
int minn=INF;
if (p1<=siz[sid]) minn=min(minn, a[blc[sid][p1]]);
if (p2<=siz[eid]) minn=min(minn, a[blc[eid][p2]]);
if (p1<=siz[sid] && minn==a[blc[sid][p1]]) sta[++top]=blc[sid][p1++];
if (p2<=siz[eid] && minn==a[blc[eid][p2]]) sta[++top]=blc[eid][p2++];
}
int lst=-INF;
for (int j=1; j<=top; ++j) if (l<=sta[j]&&sta[j]<=r)
ans=min(ans, a[sta[j]]-lst), lst=a[sta[j]];
ans=min(ans, min(min(f[l][bel[r]-1], f[r][bel[l]+1]), h[bel[l]+1][bel[r]-1]));
// cout<<f[l][bel[r]-1]<<endl;
// cout<<f[r][bel[l]+1]<<endl;
// cout<<h[bel[l]+1][bel[r]-1]<<endl;
// cout<<bel[l]+1<<' '<<bel[r]-1<<endl;
}
printf("%d\n", ans);
}
return 0;
}