Slope Trick

问题引入

CF713C
给出 n {n} n个整数 a 1 , a 2 , . . . , a n {a_1,a_2,...,a_n} a1,a2,...,an,一次操作可以选择一个整数 a i {a_i} ai使其变为 a i = a i − 1 + 1 {a_i=a_{i-1}+1} ai=ai1+1 a i = a i − 1 − 1 {a_i=a_{i-1}-1} ai=ai11,问使得 a 1 < a 2 < . . . , a n {a_1<a_2<...,a_n} a1<a2<...,an所需要的最小操作次数。
1 ≤ n ≤ 3000 1 ≤ a i ≤ 1 0 9 1\le n\le 3000\\ 1\le a_i\le 10^9 1n30001ai109

解法

a i < a i + 1 ↔ a i ≤ a i + 1 − 1 {a_i<a_{i+1}\leftrightarrow a_i\le a_{i+1}-1} ai<ai+1aiai+11
首先令 a i = a i − i a_i=a_i-i ai=aii,问题等价于使 a 1 ≤ a 2 ≤ . . . ≤ a n a_1\le a_2\le ... \le a_n a1a2...an需要的最小操作次数。
f i , x f_{i,x} fi,x为前 i {i} i个数满足 a 1 ≤ a 2 ≤ . . . ≤ a i ≤ x a_1\le a_2\le...\le a_i\le x a1a2...aix所需要的最小操作次数。
f 1 , x = m i n y ≤ x ∣ y − a 1 ∣ f i , x = m i n y ≤ x ( f i − 1 , y + ∣ y − a i ∣ ) f_{1,x}=min_{y\le x}|y-a_1|\\ f_{i,x}=min_{y\le x}(f_{i-1,y}+|y-a_i|) f1,x=minyxya1fi,x=minyx(fi1,y+yai)
f i , x f_{i,x} fi,x x x x的取值可以只考虑集合 { a 1 , a 2 , . . . , a n } \{a_1,a_2,...,a_n\} {a1,a2,...,an}内的,因此这个 d p dp dp可以在 O ( n 2 ) O(n^2) O(n2)时间内解决,但下面可以把它优化到 O ( n l o g n ) O(nlogn) O(nlogn)

slope trick

f i , x f_{i,x} fi,x x x x为横坐标, f i , x f_{i,x} fi,x为纵坐标,在 ( x , f i , x ) (x,f_{i,x}) (x,fi,x) ( x + 1 , f i , x + 1 ) (x+1,f_{i,x+1}) (x+1,fi,x+1)之间连一条直线,我们可以得到一个分段函数,这个分段函数在 x x x为整数处的斜率都为整数。

首先 f i , x f_{i,x} fi,x是一个单调递减的函数。因为若 x 1 < x 2 x_1<x_2 x1<x2,则 a 1 ≤ a 2 ≤ . . . ≤ a i ≤ x 1 ≤ x 2 a_1\le a_2\le ...\le a_i\le x_1\le x_2 a1a2...aix1x2

f i , x f_{i,x} fi,x维护一个集合 S i = { s i , i − 1 , s i , i − 2 , . . . , s i , 0 } S_i=\{s_{i,i-1},s_{i,i-2},...,s_{i,0}\} Si={si,i1,si,i2,...,si,0} f i , s 0 f_{i,s_0} fi,s0的答案。
其含义为对于 x ∈ [ s i , j , s i , j − 1 ) ( i ≤ j ≤ 0 ) x\in [s_{i,j},s_{i,j-1})(i\le j\le 0) x[si,j,si,j1)(ij0),有 f i , x + 1 − f i , x = − j f_{i,x+1}-f_{i,x}=-j fi,x+1fi,x=j(即函数 f i , x f_{i,x} fi,x在点 x x x处的斜率为 − j -j j,特别的 s i , i = − ∞ , s i , − 1 = ∞ s_{i,i}=-\infty,s_{i,-1}=\infty si,i=,si,1=)。
f i , x f_{i,x} fi,x的单调性有 s i , i − 1 ≤ s i , i − 2 ≤ . . . ≤ s i , 0 s_{i,i-1}\le s_{i,i-2}\le ...\le s_{i,0} si,i1si,i2...si,0

现在假设求出了 S i − 1 S_{i-1} Si1 f i − 1 , s 0 f_{i-1,s_0} fi1,s0,现在求 S i S_i Si,考虑三种情况:
1. x < a i 1. x< a_i 1.x<ai f i − 1 , x f_{i-1,x} fi1,x在点 x x x处的斜率为 − j ( j ≥ 0 ) -j(j\ge 0) j(j0)(即 f i − 1 , x + 1 − f i − 1 , x = − j f_{i-1,x+1}-f_{i-1,x}=-j fi1,x+1fi1,x=j
f i , x = f i − 1 , x + ( a i − x ) f i , x + 1 = f i − 1 , x + 1 + ( a i − ( x + 1 ) ) = f i − 1 , x − j + a i − x − 1 f i , x + 1 − f i , x = − j − 1 f_{i,x}=f_{i-1,x}+(a_i-x)\\ f_{i,x+1}=f_{i-1,x+1}+(a_i-(x+1))=f_{i-1,x}-j+a_i-x-1\\ f_{i,x+1}-f_{i,x}=-j-1 fi,x=fi1,x+(aix)fi,x+1=fi1,x+1+(ai(x+1))=fi1,xj+aix1fi,x+1fi,x=j1
故所有 x < a i x<a_i x<ai的点的斜率减 1 1 1,即有 s i , j = s i − 1 , j − 1 ( s i − 1 , j − 1 < a i ) s_{i,j}=s_{i-1,j-1}(s_{i-1,j-1}<a_i) si,j=si1,j1(si1,j1<ai)

2. x ≥ a i 2.x\ge a_i 2.xai f i − 1 , x f_{i-1,x} fi1,x在点 x x x处的斜率为 − j ( j > 0 ) -j(j> 0) j(j>0)
f i , x = f i − 1 , x + ( x − a i ) f i , x + 1 = f i − 1 , x + 1 + ( x + 1 − a i ) = f i − 1 , x − j + x + 1 − a i f i , x + 1 − f i , x = − j + 1 = − ( j − 1 ) f_{i,x}=f_{i-1,x}+(x-a_i)\\ f_{i,x+1}=f_{i-1,x+1}+(x+1-a_i)=f_{i-1,x}-j+x+1-a_i\\ f_{i,x+1}-f_{i,x}=-j+1=-(j-1) fi,x=fi1,x+(xai)fi,x+1=fi1,x+1+(x+1ai)=fi1,xj+x+1aifi,x+1fi,x=j+1=(j1)
故当 x ≥ a i x\ge a_i xai x x x的斜率不为 0 0 0时,它的斜率加 1 1 1,有 s i , j = s i − 1 , j + 1 ( s i − 1 , j + 1 ≥ a i & & j + 1 > 0 ) s_{i,j}=s_{i-1,j+1}(s_{i-1,j+1}\ge a_i\&\& j+1>0) si,j=si1,j+1(si1,j+1ai&&j+1>0)

3. x ≥ a i 3.x\ge a_i 3.xai f i − 1 , x f_{i-1,x} fi1,x在点 x x x处的斜率为 0 0 0
f i , x = f i − 1 , x + ( x − a i ) f i , x + 1 = f i − 1 , x + 1 + ( x + 1 − a i ) = f i − 1 , x + x + 1 − a i f i , x + 1 − f i , x = 1 f_{i,x}=f_{i-1,x}+(x-a_i)\\ f_{i,x+1}=f_{i-1,x+1}+(x+1-a_i)=f_{i-1,x}+x+1-a_i\\ f_{i,x+1}-f_{i,x}=1 fi,x=fi1,x+(xai)fi,x+1=fi1,x+1+(x+1ai)=fi1,x+x+1aifi,x+1fi,x=1
此时由于 a 1 ≤ a 2 ≤ . . . ≤ a i ≤ x ≤ x + 1 a_1\le a_2\le...\le a_i\le x\le x+1 a1a2...aixx+1,因此 f i , x + 1 f_{i,x+1} fi,x+1 f i , x f_{i,x} fi,x答案反而更优,即 f i , x + 1 = m i n ( f i , x + 1 , f i , x ) = f i , x f_{i,x+1}=min(f_{i,x+1},f_{i,x)}=f_{i,x} fi,x+1=min(fi,x+1,fi,x)=fi,x
因此如果 s i − 1 , 0 ≥ a i s_{i-1,0}\ge a_i si1,0ai,则其不需要加入到 S i S_i Si中。

基于上述结论,再分情况讨论:
1. s i − 1 , 0 < a i 1. s_{i-1,0}<a_i 1.si1,0<ai,保持集合 S i − 1 S_{i-1} Si1的元素不动。
S i − 1 = { s i − 1 , i − 2 , s i − 1 , i − 3 , . . . , s i − 1 , 0 } S_{i-1}=\{s_{i-1,i-2},s_{i-1,i-3},...,s_{i-1,0}\} Si1={si1,i2,si1,i3,...,si1,0}转为 S i = { s i , i − 1 , s i , i − 2 , . . . , s i , 1 } S_i=\{s_{i,i-1},s_{i,i-2},...,s_{i,1}\} Si={si,i1,si,i2,...,si,1},由于 a i − 1 a_i-1 ai1处的斜率变为 − 1 -1 1,但 a i a_i ai处的斜率仍然为 0 0 0,故再加入 s i , 0 = a i s_{i,0}=a_i si,0=ai
f i , s i , 0 = f i , a i = f i − 1 , a i + ∣ a i − a i ∣ = f i − 1 , a i f_{i,s_{i,0}}=f_{i,a_i}=f_{i-1,a_i}+|a_i-a_i|=f_{i-1,a_i} fi,si,0=fi,ai=fi1,ai+aiai=fi1,ai

2. s i − 1 , 0 = a i 2. s_{i-1,0}=a_i 2.si1,0=ai,保持集合 S i − 1 S_{i-1} Si1的元素不动。
s i − 1 , 0 s_{i-1,0} si1,0的斜率不变, S i − 1 = { s i − 1 , i − 2 , s i − 1 , i − 3 , . . . , s i − 1 , 0 } S_{i-1}=\{s_{i-1,i-2},s_{i-1,i-3},...,s_{i-1,0}\} Si1={si1,i2,si1,i3,...,si1,0}转为 S i = { s i , i − 1 , s i , i − 2 , . . . , s i , 2 , s i , 0 } S_i=\{s_{i,i-1},s_{i,i-2},...,s_{i,2},s_{i,0}\} Si={si,i1,si,i2,...,si,2,si,0},缺少 s i , 1 s_{i,1} si,1,加入 s i , 1 = s i , 0 = a i s_{i,1}=s_{i,0}=a_i si,1=si,0=ai即可,因为已经不存在斜率为 − 1 -1 1的整点,用 [ s i , 1 , s i , 0 ) = [ a , a ) [s_{i,1},s_{i,0})=[a,a) [si,1,si,0)=[a,a)表示这部分为空集。
f i , s i , 0 = f i , a i = f i − 1 , a i + ∣ a i − a i ∣ = f i − 1 , a i f_{i,s_{i,0}}=f_{i,a_i}=f_{i-1,a_i}+|a_i-a_i|=f_{i-1,a_i} fi,si,0=fi,ai=fi1,ai+aiai=fi1,ai

3. s i − 1 , 0 > a i 3. s_{i-1,0}>a_i 3.si1,0>ai
此时需要弹出 s i − 1 , 0 s_{i-1,0} si1,0,设 s i , k < a i , s i , k − 1 ≥ a i s_{i,k}<a_i,s_{i,k-1}\ge a_i si,k<ai,si,k1ai
S i − 1 = { s i − 1 , i − 2 , s i − 1 , i − 3 , . . . , s i , k , s i − 1 , k − 1 , . . . , s i − 1 , 1 } S_{i-1}=\{s_{i-1,i-2},s_{i-1,i-3},...,s_{i,k},s_{i-1,k-1},...,s_{i-1,1}\} Si1={si1,i2,si1,i3,...,si,k,si1,k1,...,si1,1}转为 S i = { s i , i − 1 , s i , i − 2 , . . . , s i , k + 1 , s i , k − 2 , . . . , s i , 2 , s i , 0 } S_i=\{s_{i,i-1},s_{i,i-2},...,s_{i,k+1},s_{i,k-2},...,s_{i,2},s_{i,0}\} Si={si,i1,si,i2,...,si,k+1,si,k2,...,si,2,si,0},加入 s i , k = s i , k − 1 = a i s_{i,k}=s_{i,k-1}=a_i si,k=si,k1=ai
f i , s i , 0 = f i − 1 , s i − 1 , 0 + s i − 1 , 0 − a i f_{i,s_{i,0}}=f_{i-1,s_{i-1,0}}+s_{i-1,0}-a_i fi,si,0=fi1,si1,0+si1,0ai

由于 s i , i − 1 ≤ s i , i − 2 ≤ . . . ≤ s i , 0 s_{i,i-1}\le s_{i,i-2}\le ...\le s_{i,0} si,i1si,i2...si,0,整个过程只需要维护一个优先队列,从而时间复杂度可以优化到 O ( n l o g n ) O(nlogn) O(nlogn)

#include <bits/stdc++.h>
typedef unsigned long long ull;
typedef long long ll;
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define nep(i, r, l) for (int i = r; i >= l; i--)
void sc(int &x) { scanf("%d", &x); }
void sc(int &x, int &y) { scanf("%d%d", &x, &y); }
void sc(int &x, int &y, int &z) { scanf("%d%d%d", &x, &y, &z); }
void sc(ll &x) { scanf("%lld", &x); }
void sc(ll &x, ll &y) { scanf("%lld%lld", &x, &y); }
void sc(ll &x, ll &y, ll &z) { scanf("%lld%lld%lld", &x, &y, &z); }
void sc(char *x) { scanf("%s", x); }
void sc(char *x, char *y) { scanf("%s%s", x, y); }
void sc(char *x, char *y, char *z) { scanf("%s%s%s", x, y, z); }
void out(int x) { printf("%d\n", x); }
void out(ll x) { printf("%lld\n", x); }
void out(int x, int y) { printf("%d %d\n", x, y); }
void out(ll x, ll y) { printf("%lld %lld\n", x, y); }
void out(int x, int y, int z) { printf("%d %d %d\n", x, y, z); }
void out(ll x, ll y, ll z) { printf("%lld %lld %lld\n", x, y, z); }
void ast(ll x,ll l,ll r){assert(x>=l&&x<=r);}
using namespace std;
const int N=5e3+5,mod=998244353;
const ll inf=1e18;
int n;
priority_queue<int>q;
void sol(int cas)
{
    sc(n);
	ll ans=0;
	rep(i,1,n)
	{
		int x;sc(x);
		x-=i;
		q.push(x);
		if(q.top()>x)
		{
			ans+=q.top()-x;
			q.pop();
			q.push(x);
		}
	}
	out(ans);
}
int main()
{
  // freopen("1.in", "r",stdin);
  // freopen("1.out", "w", stdout);
  srand(time(0));
  int t=1,cas=0;
//   scanf("%d",&t);
  while(t--)
  {
    sol(++cas);
  }
  return 0;
}

abc217H - Snuketoon

最初你在点 0 0 0,每一秒你可以从点 x x x走到 x + 1 , x , x − 1 x+1,x,x-1 x+1,x,x1
N N N次攻击。
i i i次攻击发生在时刻 T i T_i Ti,设此时在点 x x x,如果 D i = 0 D_i=0 Di=0,则你会受到 m a x ( 0 , X i − x ) max(0,X_i-x) max(0,Xix)点伤害, D i = 1 D_i=1 Di=1则你会受到 m a x ( 0 , x − X i ) max(0,x-X_i) max(0,xXi)点伤害。
N N N次攻击后你受到的总伤害最小是多少。
1 ≤ N ≤ 200000 1 ≤ T 1 ≤ T 2 ≤ . . . ≤ T N ≤ 1 0 9 0 ≤ D i ≤ 1 − 1 0 9 ≤ X i ≤ 1 0 9 1\le N\le 200000\\1\le T_1\le T_2\le ...\le T_N\le 10^9\\0\le D_i\le 1\\-10^9\le X_i\le 10^9 1N2000001T1T2...TN1090Di1109Xi109

f i , x f_{i,x} fi,x表示在 T i T_i Ti时刻你在点 x x x受到的最小伤害。
f i , x = c o s t ( D i , X i , x ) + min ⁡ x − ( T i − T i − 1 ) ≤ y ≤ x + ( T i − T i − 1 ) f i − 1 , y f_{i,x}=cost(D_i,X_i,x)+\min\limits_{x-(T_i-T_{i-1})\le y\le x+(T_i-T_{i-1})}f_{i-1,y} fi,x=cost(Di,Xi,x)+x(TiTi1)yx+(TiTi1)minfi1,y

与上个问题不同的是, f i , x f_{i,x} fi,x是一个下凸函数,形如下图:
在这里插入图片描述
因此这里需要维护两个优先队列。
一个为 L i = l i , i − 1 , l i , i − 2 , . . . , l i , 0 L_i={l_{i,i-1},l_{i,i-2},...,l_{i,0}} Li=li,i1,li,i2,...,li,0,另一个为 R i = r i , 0 , r i , 1 , . . . , r i , i − 1 R_i=r_{i,0},r_{i,1},...,r_{i,i-1} Ri=ri,0,ri,1,...,ri,i1
表示 x ∈ [ l i , j , l i , j − 1 ) x\in [l_{i,j},l_{i,j-1}) x[li,j,li,j1) x x x处的斜率为 − j -j j
x ∈ [ l i , 0 , r i , 0 ) x\in[l_{i,0},r_{i,0}) x[li,0,ri,0) x x x处斜率为 0 0 0
x ∈ [ r i , j , r i , j + 1 ) x\in[r_{i,j},r_{i,j+1}) x[ri,j,ri,j+1) x x x处斜率为 j j j
对于 x − ( T i − T i − 1 ) ≤ y ≤ x + ( T i − T i − 1 ) x-(T_i-T_{i-1})\le y\le x+(T_i-T_{i-1}) x(TiTi1)yx+(TiTi1)这个限制。
如图,我们对红色的图像左平移 T i − T i − 1 {T_i-T_{i-1}} TiTi1得到黄色图像,对红色的图像右平移 T i − T i − 1 {T_i-T_{i-1}} TiTi1得到粉色图像。最后我们要取的图像是棕色图像。
g i , x = min ⁡ x − ( T i − T i − 1 ) ≤ y ≤ x + ( T i − T i − 1 ) f i − 1 , y g_{i,x}=\min\limits_{x-(T_i-T_{i-1})\le y\le x+(T_i-T_{i-1})}f_{i-1,y} gi,x=x(TiTi1)yx+(TiTi1)minfi1,y只需要将 L i − 1 L_{i-1} Li1中的点坐标都减去 T i − T i − 1 T_i-T_{i-1} TiTi1,将 R i − 1 R_{i-1} Ri1中的点都加上 T i − T i − 1 T_i-T_{i-1} TiTi1就可以得到。
得到 g i , x g_{i,x} gi,x后,再将其加上分段函数 C o s t ( D i , X i ) Cost(D_i,X_i) Cost(Di,Xi),对 L i L_i Li R i R_i Ri进行形如上一题中的讨论即可,另外这里只需要维护 a n s = f i , l i , 0 = f i , l i , 0 + 1 = . . . = f i , r i , 0 ans=f_{i,l_{i,0}}=f_{i,l_{i,0}+1}=...=f_{i,r_{i,0}} ans=fi,li,0=fi,li,0+1=...=fi,ri,0的值即可。
注意这里斜率为 0 0 0的点的斜率也要加 1 1 1或减 1 1 1
另外一个地方是初始时 < 0 <0 <0处的斜率为 − ∞ -\infty > 0 >0 >0处的斜率为 ∞ \infty
所以初始时还要额外插入较多的 0 0 0
只用最左边的点来

#include <bits/stdc++.h>
typedef unsigned long long ull;
typedef long long ll;
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define nep(i, r, l) for (int i = r; i >= l; i--)
void sc(int &x) { scanf("%d", &x); }
void sc(int &x, int &y) { scanf("%d%d", &x, &y); }
void sc(int &x, int &y, int &z) { scanf("%d%d%d", &x, &y, &z); }
void sc(ll &x) { scanf("%lld", &x); }
void sc(ll &x, ll &y) { scanf("%lld%lld", &x, &y); }
void sc(ll &x, ll &y, ll &z) { scanf("%lld%lld%lld", &x, &y, &z); }
void sc(char *x) { scanf("%s", x); }
void sc(char *x, char *y) { scanf("%s%s", x, y); }
void sc(char *x, char *y, char *z) { scanf("%s%s%s", x, y, z); }
void out(int x) { printf("%d\n", x); }
void out(ll x) { printf("%lld\n", x); }
void out(int x, int y) { printf("%d %d\n", x, y); }
void out(ll x, ll y) { printf("%lld %lld\n", x, y); }
void out(int x, int y, int z) { printf("%d %d %d\n", x, y, z); }
void out(ll x, ll y, ll z) { printf("%lld %lld %lld\n", x, y, z); }
void ast(ll x,ll l,ll r){assert(x>=l&&x<=r);}
using namespace std;
const int N=2e5+5,mod=998244353;
const ll inf=1e18;
int n;
ll ans,sl,sr;
priority_queue<ll>L;
priority_queue<ll,vector<ll>,greater<ll>>R;
void sol(int cas)
{
	sc(n);
	int pre=0;
	L.push(0);
	R.push(0);
	for(int i=0;i<=n;i++) L.push(0),R.push(0);
	rep(i,1,n)
	{
		int t,d,x;sc(t,d,x);
		sl-=t-pre;
		sr+=t-pre;
		pre=t;
		if(d==0)//Xi-x
		{
			if(x<=R.top()+sr)
				L.push(x-sl);
			else
			{
				ans+=x-(R.top()+sr);
				ll y=R.top()+sr;
				R.pop();
				R.push(x-sr);
				L.push(y-sl);
			}
		}
		else//x-Xi
		{
			if(x>=L.top()+sl)
				R.push(x-sr);
			else
			{
				ans+=(L.top()+sl)-x;
				ll y=L.top()+sl;
				L.pop();
				L.push(x-sl);
				R.push(y-sr);
			}
		}
	}
	out(ans);
}
int main()
{
  // freopen("1.in", "r",stdin);
  // freopen("1.out", "w", stdout);
  srand(time(0));
  int t=1,cas=0;
//   scanf("%d",&t);
  while(t--)
  {
    sol(++cas);
  }
  return 0;
}
/*
befor submit code check:
freopen
size of N
mod
debug output
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值