DP

状压DP

首先回顾一下位运算

  • 与 &
  • 或 |
  • 异或 ^
  • 取反 ~
  • 左移 <<
  • 右移 >>

    ^ 运算的逆运算是它本身

取反是对 1 个数 $num$ 进行的计算,~ 把$num$种的0 和 1 全部取反
补码——正数的补码是其(二进制) 本身,负数的补码是其(二进制)取反后加一

右移在C++中将直接舍弃右侧多余位,左侧则较为复杂,无符号数会在左侧补0,对于有符号书,则会用最高位的数补齐
[注]:

1. 左移和右移是有返回值的,并非对$num$本身进行操作
2. 左移和右移优先级低于四则运算,`x<<1+1` 会被解释为`x<<(1+1)`,所以一般最好都加上括号

一些应用

  • num<<i 相当于$num\times 2^i$ ,而num>>i 相当于$num \div 2^i$ 。效率要比 % 和 / 操作快得多(60%?)
    • 当$num>0$时两者并没有差别,但是当$num<0$时,普通除法时向0取整,而右移是向下取整
  • num * 10 = (num << 1) + (num << 3)
  • num & 1 取num二进制末尾,判断奇偶性
  • 对应于集合上的运算
操作 集合表示 位运算语句
交集 $a\cap b$ a&b
并集 $a\cup b$ a
补集 $\overline a$ ~a
差集 $a\setminus b$ ~a
对称差 $a\triangle b$ a^b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//遍历一个集合的子集
for(int S0 = S;S0;S0 = (S0-1) & S)
//取出n二进制下的第k位
(n >> k) & 1
//取出整数n在二进制表示下第0~k-1位(后k位
n & ((1<<k) - 1)
//把整数n在二进制下的第k位取反
n ^ (1 << k)
//对整数 n 在二进制下第k位赋值1
n | (1 << k)
//对整数 n 在二进制表示下第k位赋值0
n & (~(1 << k))
//判断一个数的奇偶性
bool isOddNumber(int n){
return n & 1;
}
//判断一个数是不是2的幂
bool isFactorialofTwo(int n){
return n > 0 && (n & (n-1)) == 0;
}

好了开始正题

状压DP是DP的一种,通过状态压缩为整数来优化转移
直接上题
题目链接
在$N\times N$ 的棋盘里面放 $K$个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共$8$ 个格子。
$1\le N\le 9,0\le K\le N*N$
$f(i,j,l)$来表示前 $i$ 行,当前状态为$j$ ,且已经放置 $l$个国王时的方案。
$j$ 这一维用二进制来表示
先预处理在一行上的所有合法状态(即排除同一行上两个相邻的情况),然后直接枚举这些来匹配上一行的状态即可。
$f(i,j,l) = \sum f(i-1,x,l-num(x))$
$num(x)$ 为x在二进制下有多少个1
转移时要排除两行间国王互相攻击不合法的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int> sta,stan;
ll d[10][(1<<10)][100];
int n,k;
bool ok(int i,int j){
if(i & j)return false;
if((i << 1) & j)return false;
if(i & (j << 1))return false;
return true;
}
int main(){
scanf("%d%d",&n,&k);
for(int i=0;i<(1<<n);i++){
int num = 0;
bool flag = true;
for(int j=0;j<n-1;j++){
if(i >> j & 1){
num++;
if(i >> (j+1) & 1){
flag = false;
break;
}
}
}
if(!flag)continue;
sta.push_back(i);
stan.push_back(num + (i >> (n-1) & 1));
}
for(int i=0;i<sta.size();i++){
d[1][i][stan[i]] = 1;
}
for(int i=2;i<=n;i++){
for(int j=0;j<sta.size();j++){
for(int t=0;t<sta.size();t++){
if(ok(sta[j],sta[t])){
for(int p = stan[j];p <= k;p++){
d[i][j][p] += d[i-1][t][p-stan[j]];
}
}
}
}
}
ll res = 0;
for(int i=0;i<sta.size();i++)
res += d[n][i][k];
cout<<res<<endl;
return 0;
}

不过瘾就再来一道

Mondriann’s Dream
求把$N*M(1\le N,M \le 11)$ 的棋盘分割成若干个$1\times 2$ 的长方形,有多少种方案。例如当 $N=2,M=4$时,共有5种方案。当$N=2,M=3$时,有3种方案。

NM只有11,八九不离十可以状压了,反正得挨个铺,所以从上到下考虑。假如现在铺好了前$i$ 层,基本思想就是从$i$ 层的状态转移到$i+1$层的状态。但是该如何表示?观察一下铺满第 $i$ 层的样子(必须保证第$i$层是满的,也就是说有的可以凸出来到$i+1$层但是要保证$i$层是满的)
enter image description here
对于第 i 行中竖着放的,第 $i+1$ 层要受到牵连,它必须补全竖着放置的上一半才行。但对于横着放的,第$i+1$层则无所谓。
所以我们可以用二进制中的 1 来表示他是否是竖着放置的上一半。为0则为其他状况。
$d[i][j]$表示第 $i$ 的形态为$j$ 时,前$i$ 行分割方案的总数。 $j$ 是用十进制整数记录的 $m$ 位二进制数。考虑$i+1$行的状态$k$在满足什么情况下转移是合法的。

  • $j$中为 1 的位,$k$中必须为0
  • $j$中为 0 的位,$k$中可以为1,但 k 要是为 0,就必须是连续的偶数个0(想一想为什么)
    对于第一条,可以用 $i\&j = 0$ 来判断,对于第二条,有$z = i|j$,那么 z 的二进制表示中,每一段连续的 0 都必须有偶数个。(这些0代表若干个横着的 $1\times 2$ 长方形,奇数个0无法分割成这种形态。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <cstdio>
using namespace std;
int n,m;
long long f[12][1<<11];
bool in_s[1<<11];

int main(){
while(cin>>n>>m && n){
//先把合法状态筛出来,即二进制表示中每一段连续的0都有偶数个
for(int i=0;i<1<<m;i++){
bool cnt = 0,has_odd = 0;
for(int j=0;j<m;j++)
if(i >> j & 1)has_odd |= cnt,cnt=0;
else cnt ^= 1;
in_s[i] = (has_odd | cnt) ? 0 : 1;
}
f[0][0] = 1;
for(int i=1;i<=n;i++){
for(int j=0;j<1<<m;j++){
f[i][j] = 0;
for(int k=0;k< 1<<m;k++){
if((j & k) == 0 && in_s[j|k])
f[i][j] += f[i-1][k];
}
}
}
cout<<f[n][0]<<endl;
}
return 0;
}

数位DP

数位DP问题往往是这样的题型,给定一个闭区间$[l,r]$,让你求这个区间中满足某种条件的数的总数
又或者是求满足限制条件的第K小的数是多少。

  • 首先我们将问题转换为更加简单的形式。设$ansi$ 表示$[1,i]$ 中满足条件的数的数量,那么所求的答案就是$ans_r-ans{l-1}$。

分开求解这两个问题即可。
对于一个小于$n$的数,它从高到底肯定出现某一位使得这一位上的数值小于$n$这一位上对应的数值。而之前的所有位都和$n$上的位相等。
有了这个性质,我们可以定义$f(i,st,op)$表示当前将要考虑的是从高到低的第$i$位,当前该前缀的状态为$st$(前一位或前几位的值),前缀和当前求解的数字的大小关系是op(op=1表示等于,op=0表示小于)时的数字个数。
题目链接

windy数
windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道, 在A和B之间,包括A和B,总共有多少个windy数?$1 \le A \le B \le 2000000000$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll A,B;
int k[20],pos;//存数字各位
ll dp[20][10];//第i位为j的数字个数(不能大小限制)
/*
pos : 当前考虑的第pos位,例如十进制数12345,5为第0位
pre : 当前位的前一位的数字
lead : 是否有前导0,比如之前枚举的两位 00xxx,在枚举3位时是有前导0的。
limit : 是否有大小限制,比如枚举了12xxx,在枚举3位时最大枚举到3
*/
ll dfs(int pos,int pre,bool lead,bool limit){
if(pos == -1) return 1;
if(!limit && !lead && dp[pos][pre])return dp[pos][pre];
int up = limit ? k[pos] : 9;//确定枚举上界
ll res = 0;
for(int i=0;i<=up;i++){
if(lead){
if(i == 0){
res += dfs(pos-1,0,true,false);
}
else{
res += dfs(pos-1,i,false,limit && i == k[pos]);
}
}else{
if(abs(i-pre) < 2)continue;
res += dfs(pos-1,i,false,limit && i == k[pos]);
}
}
if(!limit && !lead)dp[pos][pre] = res;
return res;
}
ll solve(ll x){
int pos = 0;
while(x){
k[pos++] = x % 10;
x /= 10;
}
return dfs(pos-1,0,true,true);
}
int main(){
scanf("%lld%lld",&A,&B);
printf("%lld\n",solve(B) - solve(A-1));
return 0;
}

Valley Number
当一个数字,从左到右依次看过去数字没有出现先递增接着递减的“山峰”现象,就被称作 Valley Number。它可以递增,也可以递减,还可以先递减再递增。在递增或递减的过程中可以出现相等的情况。
比如,1,10,12,212,32122都是 Valley Number。
121,12331,21212则不是。
度度熊想知道不大于N的Valley Number数有多少。
注意,前导0是不合法的。
每组数据包含一个数N。
● 1≤T≤200
● 1≤length(N)≤100
结果对$1000000007$取模

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
typedef long long ll;
int T;
ll d[101][11][3];
char s[101];
int k[101],pos;
/*
减后可以增。增后不能减
status :
0 不增不减
1 增
2 减
*/
ll dfs(int pos,int pre,int status,bool lead,bool limit){
if(pos == -1)return lead ? 0 : 1;
if(!lead && !limit && d[pos][pre][status])return d[pos][pre][status];

int up = limit ? k[pos] : 9;
ll res = 0;
for(int i=0;i<=up;i++){
if(lead){
if(i == 0){
(res += dfs(pos-1,0,0,true,false))%mod;
}
else{
(res += dfs(pos-1,i,0,false,limit && i == k[pos]))%mod;
}
}
else{
if(i < pre){
if(status == 1)continue;
(res += dfs(pos-1,i,2,false,limit && i == k[pos]))%mod;
}
else if(i == pre){
(res += dfs(pos-1,i,status,false,limit && i == k[pos]))%mod;
}
else{
(res += dfs(pos-1,i,1,false,limit && i == k[pos]))%mod;
}
}
}
if(!lead && !limit) d[pos][pre][status] = res % mod;
return res;
}
ll solve(){
int n = strlen(s);
pos = 0;
for(int i=n-1;i>=0;i--)k[pos++] = s[i]-'0';
return dfs(pos-1,0,0,true,true);
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%s",s);
printf("%lld\n",solve()%mod);
}
return 0;
}

POJ-3208 启示录
只要某数字的十进制表示中有三个6相邻,则该数字为魔鬼数,求第X小的魔鬼数$X\le 5e7$

这一类题目可以先用DP进行预处理,再基于拼凑思想,用“试填法”求出最终的答案

$F[i,3]$表示由 $i$ 位数字构成的魔鬼数有多少个,$Fi,j$ 表示由 $i$ 位数字构成的,开头已经有连续 $j$ 个6的非魔鬼数有多少个。(允许前导0的存在,想一想为什么)
转移方程

  1. $F[i,0] = 9*(F[i-1,0] + F[i-1,1] + F[i-1,2])$
  2. $F[i,1] = F[i-1,0]$
  3. $F[i,2] = F[i-1,1]$
  4. $F[i,3] = F[i-1,2] + 10 * F[i-1,3]$

然后一位一位的试填,要注意前面填过的数字结尾如果有 k 个6,通过后面拼接 3-k 个6也可以构成魔鬼数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
ll f[21][4];
int T,n,l;
void init(){
f[0][0] = 1;
for(int i=1;i<=20;i++){
f[i][0] = 9*(f[i-1][0] + f[i-1][1] + f[i-1][2]);
f[i][1] = f[i-1][0];
f[i][2] = f[i-1][1];
f[i][3] = f[i-1][2] + 10 * f[i-1][3];
}
}
int main(){
init();
scanf("%d",&T);
while(T--){
scanf("%d",&n);
//l为答案的长度
for(l=3;f[l][3] < n;l++);
//k表示填过的数字末尾有k个6
for(int i=l,k=0;i;i--){
for(int j=0;j<=9;j++){
ll cnt = f[i-1][3];//后面预处理出的魔鬼数
//找能够拼凑出来的魔鬼数
if(j == 6 || k == 3){
if(k == 3){
for(int x = 0;x < 3;x++)
cnt += f[i-1][x];
}else{
for(int x = max(3-k-1, 0);x<3;x++){
cnt += f[i-1][x];
}
}
}
if(cnt < n) n -= cnt;
else{
if(k < 3) j == 6 ? k ++ : k=0;
printf("%d",j);break;
}
}
}
cout<<endl;
}
return 0;
}

BZOJ1799 月之迷
给出两个数a,ba,b,求出$[a,b]$中各位数字之和能整除原数的数的个数。
我们按照模板的做法来想,枚举到第pos位时,要确定这一位的数字,可以更新现在所填数字的和,但对于最终的和无从得知,是否能整除也无从判别,我们试着先确定了最终的和,在枚举每一位的时候注意到,枚举x,则对最终和模数可以更新为 $(mod * 10 + x) \% sum$ ,所以可以想到每一次枚举一个和sum
$d[i][j][k]$表示 i 位数字,前面填过的数字和为 j 时,模sum为 k 的数字个数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll d[20][160][160],L,R;
int k[20],pos,mod;
//sum为之前填过的数字的和,p为之后要填的数字对mod的模为p
ll dfs(int pos,int sum,int p,bool lead,bool limit){
if(pos == -1){
if(p == 0 && sum == mod)return 1;
else return 0;
}
if(!limit && !lead && d[pos][sum][p] != -1)return d[pos][sum][p];
int up = limit ? k[pos] : 9;
ll res = 0;
for(int i = 0;i <= up;i ++){
if(lead){
if(i == 0){
res += dfs(pos - 1, sum + i, p, true, false);
}
else {
res += dfs(pos - 1, sum + i, (p * 10 + i) % mod, false, limit && i == k[pos]);
}
}
else {
res += dfs(pos - 1,sum + i, (p * 10 + i) % mod, false, limit && i == k[pos]);
}
}
if(!limit && !lead) d[pos][sum][p] = res;
return res;
}

ll solve(ll x){
pos = 0;
while(x){
k[pos++] = x % 10;
x/=10;
}
ll res = 0;
//mod为当前枚举的和
for(mod=1;mod <= pos * 9;mod++){
memset(d,-1,sizeof d);
res += dfs(pos-1,0,0,true,true);
}
return res;
}
int main(){
scanf("%lld%lld",&L,&R);
//cout<<solve(R)<<' ' <<solve(L-1)<<endl;
printf("%lld\n",solve(R)-solve(L-1));
return 0;
}

计数类DP

直接上题,跟之前的排列组合有比较大的联系

CF-559C Gerald and Giant Chess
给定一个 $H*W$的棋盘,棋盘上只有$N$ 个格子是黑色的,其他格子都是白色的。
在棋盘左上角有一个卒,每一步可以向右或者向下移动一格,并且不能移动到黑色格子中。求这个卒从左上角移动到右下角,一共有多少种可能的路线
$1\le H,W\le 10^5,1\le N\le 2000$ 输出对$10^9+7$取模
H,W巨大,普通DP不用想,考虑如何用黑格子计数

黑格子

由组合数学知识可知,从S到T的总路径条数为$C{H+W-2}^{H-1}$,只要减去至少经过一个黑格子的路径条数即为答案。
那么如何不重不漏的计数呢?
考虑每条至少经过一个黑格子的路径所包含的第一个黑格子,以4号黑格子(4,5)为例,从S到4号,总路径条数有$C
{4+5-1-1}^{4-1}$条,只要排除掉经过3和经过1的路径条数即为从S到4,不经过黑格子的路径数。如何排除?其实我们之前已经算出来了,在算S到4的不经过黑格子路径条数时,已经分别算过了S到3,S到1的不经过黑格子路径条数,只要分别乘上由3到4,由1到4的所有路径数即可。

把所有黑色格子按照行列坐标递增的顺序排序,设$f[i]$ 为从S到第 $i$个格子,途中不经过其他黑色格子的路径数

在求解计数类动态规划时,通常要找一个“基准点”,围绕这个基准点构造一个不可划分的”整体”,以避免子问题之间的重叠

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
const int N = 2e5+10;
typedef long long ll;
typedef pair<int,int> pii;
#define fi first
#define se second
ll jc[N],inv[N];
int h,w,n;
ll f[2010];
pii a[2010];
ll ksm(ll a,ll b){
ll res = 1;
for(;b;b>>=1){
if(b & 1)res = res * a % mod;
a = a * a % mod;
}
return res;
}
int C(int x,int y){
return jc[x] * inv[y] %mod * inv[x-y] % mod;
}
int main(){
jc[0] = 1;inv[0] = 1;
for(int i=1;i<N;i++)jc[i] = jc[i-1] * i % mod,inv[i] = ksm(jc[i],mod-2);
scanf("%d%d%d",&h,&w,&n);
for(int i=1;i<=n;i++)scanf("%d%d",&a[i].fi,&a[i].se);

sort(a+1,a+1+n);
a[n+1].fi = h;a[n+1].se = w;

for(int i=1;i<=n+1;i++){
int x = a[i].fi,y = a[i].se;
f[i] = C(x+y-2,x-1);
for(int j=1;j<i;j++){
int xj = a[j].fi;
int yj = a[j].se;
if(xj > x || yj > y)continue;
f[i] = (f[i] - (ll)f[j] * C(x-xj+y-yj,x-xj)%mod + mod)%mod;
}
}
printf("%lld\n",f[n+1]%mod);
return 0;
}

斜率优化DP

P3195 [HNOI2008]玩具装箱TOY

题目链接
设$d[i]$为将前 $i$ 个玩具装入箱中所需得最小费用
容易得到动态转移方程:

其中$s[i] = \sum_1^iC[i]$,普通DP复杂度为$O(n^2)$。经过斜率优化后将变为$O(n)$。
仔细观察我们便于表示可以令$f[i] = s[i]+i$
那么式子变成了

我们讨论$j_1,j_2(1\le j_1< j_2<i)$决策,假设$j_2$要比$j_1$更优,那么有

$d[j_1] + (f[i] -f[j_1]-1-L)^2 \ge d[j_2]+(f[i]-f[j_2]-1-L)^2$

展开后得到

$d[j_1] + f[i]^2 - 2\times f[i]\times (f[j_1]+1+L)+(f[j_1]+1+L)^2 \ge d[j_2]+f[i]^2-2\times f[i]\times (f[j_2]+1+L)+(f[j_2]+1+L)^2$

移项后可得

$2\cdot f[i]\ge {d[j_2]+(f[j_2]+1+L)^2-d[j_1]-(f[j_1]+1+L)^2 \over f[j_2]-f[j_1]}$

令$g[i] = f[i]+1+L$, 则有

$2\cdot f[i]\ge {(d[j_2]+g[j_2])-(d[j_1]+g[j_1])\over f[j_2]-f[j_1]}$

所以用一个队列维护决策集,当$j_1<j_2$,并且上式满足时,$j_1$ 出队。
又由于$f[i]$随$i$单调递增。所以计算$d[i]$之后要将 $i$ 入队时,要及时排除掉不可能作为决策的元素。
如何计算?队尾的斜率也要满足单调性,保持跟$f[i]$的单调性一致即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <bits/stdc++.h>
using namespace std;
const int N = 50010;
typedef long long ll;
typedef long double db;
db c[N],d[N],f[N],s[N],g[N];
int n,L;
int q[N],l,r;
db sqr(db x){return x * x;}
db slope(int i,int j){
return ((d[i] + g[i]) - (d[j] + g[j])) / (f[i] - f[j]);
}
int main(){
scanf("%d%d",&n,&L);
l=r=1;
for(int i=1;i<=n;i++){
cin>>c[i];
s[i]=s[i-1] + c[i];
f[i] = s[i] + i;
g[i] = (f[i] + 1 + L) * (f[i] + 1 + L);
}
g[0] = (ll)(1+L)*(1+L);//注意0号元素的g值初始化
for(int i=1;i<=n;i++){
while(l < r && slope(q[l],q[l+1]) < 2 * f[i])l++;
int j = q[l];
d[i] = d[j] + sqr(f[i]-f[j]-1-L);
while(l < r && slope(q[r],q[r-1]) > slope(i,q[r-1]))r--;//满足队尾斜率单调性
q[++r] = i;//入队
}
printf("%lld\n",(ll)d[n]);
return 0;
}

P2120 [ZJOI2007]仓库建设

题意:$1\sim N$ 号工厂,第$i$ 个工厂有$P_i$个成品,第$i$个工厂建立仓库需要$C_i$的费用,该工厂距离第一个工厂的距离为$X_i$,编号小的工厂只能往编号大的工厂搬用成品,每单位成品搬每单位距离需要花费1,问所有成品搬到工厂里面所需的最少费用是多少

分析
设$f[i]$ 为第 i 个工厂建立仓库,前 i 个工厂的成品都搬到仓库中的最小花费,则容易得到动态转移方程:

通式为

令 $s[i] = \sum_1^i P[i], ~~g[i] = \sum_1^iP_i\cdot X_i$,
则方程变为

则对于最优决策 $j$ ,有

也就是要找 $y = kx+b$,$k$已知,找一对$x,y$使得截距最小
由于$X[i]$是随$i$递增的,所以要维护的决策集的斜率也是递增的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
typedef long long ll;
ll C[N],P[N],X[N],f[N],s[N],g[N];
int n;
int q[N],l,r;
long double slope(int i,int j){
return (long double)((f[i]+g[i]) - (f[j]+g[j]))/(s[i]-s[j]);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld%lld%lld",&X[i],&P[i],&C[i]);
s[i] = s[i-1] + P[i];
g[i] = g[i-1] + P[i] * X[i];
}
l = r = 0;
for(int i=1;i<=n;i++){
while(l < r && slope(q[l],q[l+1]) <= X[i])l++;
int j = q[l];
f[i] = f[j] + (s[i-1] - s[j]) * X[i] - g[i-1] + g[j] + C[i];
while(l < r && slope(q[r-1],q[r]) > slope(q[r-1],i))r--;
q[++r] = i;
}
printf("%lld\n",f[n]);
return 0;
}