题目
Description
在炽热的核熔炉中,居住着一位少女,名为灵乌路空。
据说,从来没有人敢踏入过那个熔炉,因为人们畏缩于空所持有的力量——核能。
核焰,可融真金。
咳咳。
每次核融的时候,空都会选取一些原子,排成一列。然后,她会将原子序列分成一些段,并将每段进行一次核融。
一个原子有两个属性:质子数和中子数。
每一段需要满足以下条件:
1、同种元素会发生相互排斥,因此,同一段中不能存在两个质子数相同的原子。
2、核融时,空需要对一段原子加以防护,防护罩的数值等于这段中最大的中子数。换句话说,如果这段原子的中子数最大为x,那么空需要付出x的代价建立防护罩。求核融整个原子序列的最小代价和。
Input
第一行一个正整数N,表示原子的个数。
接下来N行,每行两个正整数pi和ni,表示第i个原子的质子数和中子数。
Output
输出一行一个整数,表示最小代价和。
Sample Input
5
3 11
2 13
1 12
2 9
3 13
Sample Output
26
Data Constraint
对于20%的数据,1<=n<=100
对于40%的数据,1<=n<=1000
对于100%的数据,1<=n<=10^5,1<=pi<=n,1<=ni<=2*10^4
大概意思就是说把一些数分成若干段,每一段中的a[i]两两不能相同,代价为 max(b[i]) ,求最小总代价。
20%
暴力乱搞。。。
40%
DP,设F[i]表示当前段结尾为i的最小代价和。
则可以从当前F[i]推向F[j],中途统计一下最大值再转移。
80%
用一个单调队列来存可能的每个转移位置,把其中位置靠前而且答案更不优的位置删除。
好吧这其实是水法但是我没打2333
100%
DP设F[i]表示当前段结尾为i的最小代价和(和40%一样)。
其中状态转移方程为
F[i]=F[j]+max(b[j+1]...b[i])(j=last...i−1)
其中last表示最远的转移位置。
很显然从i往前的max(b[j+1]…b[i])一定是单调不降的。
阶梯表示max(b[j+1]…b[i])
每个圆圈的影响范围就是
(因为每个圈不会影响到自己,但是会影响到下一个圈)
所以可以用线段树维护F[i]+b[i]的最小值和F[i]的值,再用栈维护阶梯。
大致流程
比如要加入i+1个位置
那么就这样修改max(b[j])的值
然后统计min(F[j]+b[j])
再在线段树上修改F[i]的值
还有last的求法就自己脑补了啊2333
code
#include <iostream>
#include <cstdio>
#include <cstring>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define Max_len 524288
using namespace std;
int a[100001];
int b[100001];
int f[100001];
int tr[Max_len][3];
int d[100001];
int num[100001];
int ls[100001];
int n,i,j,t,l,sum;
int max(int x,int y)
{
if (x>y)
return x;
else
return y;
}
int min(int x,int y)
{
if (x<y)
return x;
else
return y;
}
void down(int t)
{
if (tr[t][2]!=(-1))
{
tr[t*2][2]=tr[t][2];
tr[t*2+1][2]=tr[t][2];
tr[t][1]=tr[t][0]+tr[t][2];
tr[t][2]=-1;
}
}
void change0(int t,int l,int r,int id,int s)
{
if (l==r)
{
tr[t][0]=s;
return;
}
down(t*2);
down(t*2+1);
int mid=(l+r)/2;
if (id<=mid)
change0(t*2,l,mid,id,s);
else
change0(t*2+1,mid+1,r,id,s);
tr[t][0]=min(tr[t*2][0],tr[t*2+1][0]);
}
void change1(int t,int l,int r,int x,int y,int s)
{
if (y<x)
return;
if (l<r)
{
down(t*2);
down(t*2+1);
}
if ((x<=l) && (r<=y))
{
tr[t][2]=s;
down(t);
return;
}
int mid=(l+r)/2;
if (x<=mid)
change1(t*2,l,mid,x,y,s);
if (mid<y)
change1(t*2+1,mid+1,r,x,y,s);
tr[t][1]=min(tr[t*2][1],tr[t*2+1][1]);
}
void find(int t,int l,int r,int x,int y)
{
if (y<x)
return;
if (l<r)
{
down(t*2);
down(t*2+1);
tr[t][1]=min(tr[t*2][1],tr[t*2+1][1]);
}
if ((x<=l) && (r<=y))
{
sum=min(sum,tr[t][1]);
return;
}
int mid=(l+r)/2;
if (x<=mid)
find(t*2,l,mid,x,y);
if (mid<y)
find(t*2+1,mid+1,r,x,y);
}
int main()
{
freopen("array.in","r",stdin);
freopen("array.out","w",stdout);
fo(i,1,Max_len-1)
tr[i][0]=2000000000,tr[i][1]=2000000000;
scanf("%d",&n);
fo(i,1,n)
scanf("%d%d",&a[i],&b[i]),ls[i]=num[a[i]],num[a[i]]=i;
f[1]=b[1];
t=1;
d[0]=0;
d[1]=1;
l=0;
change0(1,0,n,0,0);
change0(1,0,n,1,f[1]);
change1(1,0,n,0,1,b[1]);
fo(i,2,n)
{
l=max(l,ls[i]);//维护last
while ((t>0) && (b[d[t]]<=b[i]) && (d[t]>=l))
t--;
change1(1,0,n,d[t],i-1,b[i]);
d[++t]=i;
sum=233333333;
find(1,0,n,l,i-1);
f[i]=sum;
change0(1,0,n,i,f[i]);
}
printf("%d\n",f[n]);
}