专栏文章
题解:AT_abc430_d [ABC430D] Neighbor Distance
AT_abc430_d题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @min8qmjp
- 此快照首次捕获于
- 2025/12/01 22:24 3 个月前
- 此快照最后确认于
- 2025/12/01 22:24 3 个月前
权值线段树写法
题意
有一条数轴,初始时只有编号为 的人站在坐标 处。
接着,编号为 的人依次到达,并站在数轴上。第 个人站在坐标 ,且所有 互不相同)。
接着,编号为 的人依次到达,并站在数轴上。第 个人站在坐标 ,且所有 互不相同)。
每当一个人到达后,需要回答以下问题:
假设当前数轴上有 个人(编号 ),定义每个人 的 为该人到最近另一个人的距离(即 )。
要求计算当前所有 个人的 的总和,即 。
假设当前数轴上有 个人(编号 ),定义每个人 的 为该人到最近另一个人的距离(即 )。
要求计算当前所有 个人的 的总和,即 。
分析
我们在全局开一个 ,记录当前答案。
考虑加入一个人会造成什么影响? 注意到新加入一个人 只会影响到当前他左边第一个人 和右边第一个人 。
我们设 到 的距离为 , 到 的距离为 。
显然 就等于 ,如果 小于 就更新;同样的,若 小于 ,相应的它们的贡献也会从 中加或删去。
所以问题转化为
- 找到一个点左边的第一个点和它的坐标,右边的第一个的和它的坐标。
- 加入这个点。
权值线段树
顾名思义,把权值看作下标,这里我们需要一些小技巧来完成这个题。
CPPfor(int i=1;i<=n;i++){
int p = lower_bound(b+1,b+n+1,x[i]) - b;
pos[i] = p; val[p] = x[i];
}
我们将坐标看作线段树中的区间下标。
将坐标离散化, 是坐标对应的离散值, 记录 ,用于顺序插入坐标; 表示 所对应的原坐标值,用于计算距离。
将坐标离散化, 是坐标对应的离散值, 记录 ,用于顺序插入坐标; 表示 所对应的原坐标值,用于计算距离。
查询 的左边第一个数,等价于在 中查询最大的出现过的值;查询右边第一个则等价于在 中查询最小的出现过的值。用权值树维护区间 ,每单点插入更新一下最值即可。
时间复杂度 ,可以通过。
细节自己要好好处理一下。
CPP#include<bits/stdc++.h>
#define ll long long
#define mid ((l+r)>>1)
#define ls rt<<1
#define rs rt<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define fi first
#define se second
using namespace std;
const int N = 1e6 + 10;
template<typename TY>
inline void read(TY &s){
ll x = 0,f = 1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
s = x * f;
}
void _write(ll x){
if(x < 0) putchar('-'),x=-x;
if(x > 9) _write(x/10);
putchar(x%10+'0');
}
inline void write(ll x,char ch){
_write(x); cout << ch;
}
int n,x[N],b[N];
ll dis[N],ans;
ll val[N],pos[N];
int minn[N<<2],maxn[N<<2];
void update(int rt,int l,int r,int v){
minn[rt] = min(minn[rt],v); maxn[rt] = max(maxn[rt],v);
if(l == r) return;
if(v <= mid) update(lson,v);
else update(rson,v);
}
int query_r(int rt,int l,int r,int ql,int qr){
if(ql <= l && r <= qr) return maxn[rt];
int res = -1;
if(ql <= mid) res = max(res,query_r(lson,ql,qr));
if(mid + 1 <= qr) res = max(res,query_r(rson,ql,qr));
return res;
}
int query_l(int rt,int l,int r,int ql,int qr){
if(ql <= l && r <= qr) return minn[rt];
int res = 0x3f3f3f3f;
if(ql <= mid) res = min(res,query_l(lson,ql,qr));
if(mid + 1 <= qr) res = min(res,query_l(rson,ql,qr));
return res;
}
int main(){
read(n);
for(int i=1;i<=n;i++){
read(x[i]);
b[i] = x[i];
}
sort(b+1,b+n+1);
for(int i=0;i<=n;i++) dis[i] = 1e18;
for(int i=1;i<=n;i++){
int p = lower_bound(b+1,b+n+1,x[i]) - b;
pos[i] = p; val[p] = x[i];
}
memset(minn,0x3f,sizeof minn);
memset(maxn,-1,sizeof maxn);
update(1,0,n,0);
ans = 1e18;
for(int i=1;i<=n;i++){
int p = pos[i];
int l = query_r(1,0,n,0,p),r = query_l(1,0,n,p,n);//找出最小的 l 和 最大的 r
if(l != -1){
// cerr << p << "->";
ll disl = val[p]-val[l];
// cerr << dis[p] << "disl->";
dis[p] = min(dis[p],disl);
if(dis[l] > disl){
ans -= dis[l];
dis[l] = disl;
ans += dis[l];
}
// cerr << ans << "ans_l\n";
}
if(r != 0x3f3f3f3f){
// cerr << r << "r\n";
ll disr = val[r]-val[p];
dis[p] = min(dis[p],disr);
if(dis[r] > disr){
ans -= dis[r];
dis[r] = disr;
ans += dis[r];
}
}
update(1,0,n,p);//单点更新 p
ans += dis[p];
// cerr << dis[p] << "dis[p]\n";
cout << ans << "\n";
}
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...