题意:
有
N
N
个高为的矩形,第
i
i
个矩形的y轴范围为,
x
x
轴范围为。
需要横向移动一些矩形,使得所有矩形是连通的(角也算),对于一个矩形,横向移动
x
x
距离的代价为,求出最小代价。
题解:
设
fi,j
f
i
,
j
表示
y
y
轴坐标为 ,
x
x
轴的坐标为
j
j
,且以下全部联通的方案数。
那么有:
fi,p=|ri−p|+minp−leni≤j,p≥j−leni−1fi−1,j
f
i
,
p
=
|
r
i
−
p
|
+
min
p
−
l
e
n
i
≤
j
,
p
≥
j
−
l
e
n
i
−
1
f
i
−
1
,
j
整理一下 :
fi,p=|ri−p|+minj−leni−1≤p≤j+lenifi−1,j
f
i
,
p
=
|
r
i
−
p
|
+
min
j
−
l
e
n
i
−
1
≤
p
≤
j
+
l
e
n
i
f
i
−
1
,
j
把它看做两部分的函数相加, 第一部分为绝对值函数
gi=|x−ri|
g
i
=
|
x
−
r
i
|
, 第二部分为
fi−1
f
i
−
1
, 相当于是
fi−1
f
i
−
1
左右平移后加上
gi
g
i
,用归纳法容易证明这个函数是分段一次函数, 且斜率依次为
−i+1−i+2,...,0,...i−2,i−1
−
i
+
1
−
i
+
2
,
.
.
.
,
0
,
.
.
.
i
−
2
,
i
−
1
。
然后维护这个分段函数的分界点即可。 我们维护对顶堆来分别维护直线下降时的端点和直线上升时的端点,平移直接打
tag
t
a
g
即可,加入一个绝对值函数则根据函数位于左,右,中间时分别讨论, 可以方便的在讨论的时候更新答案。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
inline int rd() {
char ch=getchar(); int i=0,f=1;
while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
return i*f;
}
const int N=1e5+50;
int n,l[N],r[N];
LL ans,tl,tr,lq,rq;
priority_queue < LL,vector<LL>,less<LL> > L;
priority_queue < LL,vector<LL>,greater<LL> > R;
int main() {
n=rd();
for(int i=1;i<=n;i++) l[i]=rd(), r[i]=rd(), l[i]=r[i]-l[i];
L.push(r[1]); R.push(r[1]);
for(int i=2;i<=n;i++) {
tl-=l[i-1], tr+=l[i];
lq=L.top()+tl; rq=R.top()+tr;
if(r[i]>=lq&&r[i]<=rq) L.push(r[i]-tl), R.push(r[i]-tr);
else if(r[i]>=lq) {
ans+=r[i]-rq; R.pop(); L.push(rq-tl);
R.push(r[i]-tr); R.push(r[i]-tr);
} else {
ans+=lq-r[i]; L.pop(); R.push(lq-tr);
L.push(r[i]-tl); L.push(r[i]-tl);
}
} cout<<ans;
}