title
【问题描述】
在炽热的核熔炉中,居住着一位少女,名为灵乌路空。
据说,从来没有人敢踏入过那个熔炉,因为人们畏缩于空所持有的力量——核能。
核焰,可融真金。
每次核融的时候,空都会选取一些原子,排成一列。然后,她会将原子序列分成一些段,并将每段进行一次核融。
一个原子有两个属性:质子数和中子数。
每一段需要满足以下条件:
1、同种元素会发生相互排斥,因此,同一段中不能存在两个质子数相同的原子。
2、核融时,空需要对一段原子加以防护,防护罩的数值等于这段中最大的中子数。换句话说,如果这段原子的中子数最大为x,那么空需要付出x的代价建立防护罩。求核融整个原子序列的最小代价和。
【输入】
第一行一个正整数N,表示原子的个数。
接下来N行,每行两个正整数pi和ni,表示第i个原子的质子数和中子数。
【输出】
输出一行一个整数,表示最小代价和。
【输入输出样例1】
array.in
5
3 11
2 13
1 12
2 9
3 13
array.out
26
【数据范围】
对于20%的数据,1<=n<=100
对于40%的数据,1<=n<=1000
对于100%的数据,1<=n<=105,1<=pi<=n,1<=ni<=2*104
analysis
显然有
。设
为在
与
之间切分最小代价和,那么
就是答案。
为 点向左能到的位置。
举个例子:
现在
发现能转移的为
也就是以
为该区间的代价的,左边界为下一个比它大的右边。
(
)
现在我们到了下个
假设先在i能到达的l[i]=3,
现在转移变为
显然只改变了前面的装移{因为l改变且递增},
把后面的
的状态踢掉了,剩下的不变
。
也就是说对于前一个状态,一些转移不变,也就是这部分的最大值已经算出,维护一下就
了。
——Amber_lylovely
线段树写法:gmh77
code
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
multiset<int>t;
int a[maxn],b[maxn];
int hou[maxn],l[maxn],data[maxn];
int sum[maxn],wei[maxn],f[maxn];
int main()
{
freopen("array.in","r",stdin);
freopen("array.out","w",stdout);
int n;read(n);
for (int i=1; i<=n; ++i) read(a[i]),read(b[i]);
for (int i=1; i<=n; ++i) l[i]=hou[a[i]]+1,hou[a[i]]=i;
for (int i=1; i<=n; ++i) l[i]=max(l[i-1],l[i]);
int head=1,tail=0;
for (int i=1; i<=n; ++i)
{
int k=i-1;
while (head<tail && wei[head+1]<l[i])
{
t.erase(t.find(sum[head]));
++head;
}
while (head<=tail && b[i]>data[tail])
{
t.erase(t.find(sum[tail]));
k=wei[tail];
--tail;
}
data[++tail]=b[i];
wei[tail]=k;
if (head!=tail)
{
sum[tail]=f[wei[tail]]+data[tail];
t.insert(sum[tail]);
t.erase(t.find(sum[head]));
}
sum[head]=f[l[i]-1]+data[head];
t.insert(sum[head]);
f[i]=*t.begin();
}
printf("%d\n",f[n]);
return 0;
}