Description
在炽热的核熔炉中,居住着一位少女,名为灵乌路空。
据说,从来没有人敢踏入过那个熔炉,因为人们畏缩于空所持有的力量——核能。
核焰,可融真金。
咳咳。
每次核融的时候,空都会选取一些原子,排成一列。然后,她会将原子序列分成一些段,并将每段进行一次核融。
一个原子有两个属性:质子数和中子数。
每一段需要满足以下条件:
1、同种元素会发生相互排斥,因此,同一段中不能存在两个质子数相同的原子。
2、核融时,空需要对一段原子加以防护,防护罩的数值等于这段中最大的中子数。换句话说,如果这段原子的中子数最大为x,那么空需要付出x的代价建立防护罩。求核融整个原子序列的最小代价和。
Input
第一行一个正整数N,表示原子的个数。
接下来N行,每行两个正整数pi和ni,表示第i个原子的质子数和中子数。
Output
输出一行一个整数,表示最小代价和。
Hint
Data Constraint
对于20%的数据,1<=n<=100
对于40%的数据,1<=n<=1000
对于100%的数据,1<=n<=10^5,1<=pi<=n,1<=ni<=2*10^4
Solution
题目一股中二气息
这里可以看出是要把原子分段,我们考虑dp设f[i]为前i项都核融的最小代价,那么f[i]=min{f[j]+mx(j+1,i)}满足(last[i]<=j < i),这里的last[i]是预处理的由i向左拓展的最远距离(也就是不含同类原子的最远距离),会T
观察一下可以发现当右端点i固定左端点j向左扫时其间b的最大值mx是不减的,这就相当于在找到下一个更大值之前mx不会变。这里维护一个递减的单调队列表示从i向左扫到那些位置会使mx发生变化。由于f是单调不减的,在mx不变时用最左边的f一定是最小值,这就相当于在单调队列中的每一项的f加上下一位的mx
由于某些神奇的原因这题暴力修改单调队列中的元素不会T,就放上来了
Code
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#define rep(i, st, ed) for (int i = st; i <= ed; i += 1)
#define drp(i, st, ed) for (int i = st; i >= ed; i -= 1)
#define erg(i, st) for (int i = ls[st]; i; i = e[i].next)
#define fill(x, t) memset(x, t, sizeof(x))
#define max(x, y) ((x)>(y)?(x):(y))
#define min(x, y) ((x)<(y)?(x):(y))
#define abs(x) ((x)<(0)?(-(x)):(x))
#define ll long long
#define ld lont double
#define db double
#define INF 0x3f3f3f3f
#define N 1000001
#define E 10001
int queue[N], last[N], a[N], b[N], f[N], pos[N];
inline int read() {
int x = 0, v = 1;
char ch = getchar();
for (; ch < '0' || ch > '9'; v *= ((ch == '-')?(-1):(1)), ch = getchar());
for (; ch <= '9' && ch >= '0'; (x *= 10) += ch - '0', ch = getchar());
return x * v;
}
int main(void) {
// freopen("data.in","r",stdin);
// freopen("myp.out","w",stdout);
int n = read();
int head = 0;
int tail = -1;
rep(i, 1, n) {
a[i] = read();
b[i] = read();
if (!pos[a[i]]) {
pos[a[i]] = i;
last[i] = 0;
} else {
last[i] = pos[a[i]];
pos[a[i]] = i;
}
last[i] = max(last[i], last[i - 1]);
}
queue[++ tail] = 1;
f[1] = b[1];
rep(i, 2, n) {
while (last[i] > queue[head] && head <= tail) {
head += 1;
}
while (b[queue[tail]] <= b[i] && head <= tail) {
tail -= 1;
}
queue[++ tail] = i;
f[i] = INF;
rep(j, head + 1, tail) {
f[i] = min(f[queue[j - 1]] + b[queue[j]], f[i]);
}
f[i] = min(f[last[i]] + b[queue[head]], f[i]);
}
printf("%d\n", f[n]);
return 0;
}