题目描述
每次核融的时候,空都会选取一些原子,排成一列。然后,她会将原子序列分成一些段,并将每段进行一次核融。
一个原子有两个属性:质子数和中子数。
每一段需要满足以下条件:
1、同种元素会发生相互排斥,因此,同一段中不能存在两个质子数相同的原子。
2、核融时,空需要对一段原子加以防护,防护罩的数值等于这段中最大的中子数。换句话说,如果这段原子的中子数最大为x,那么空需要付出x的代价建立防护罩。求核融整个原子序列的最小代价和。
一道典型的线段树维护DP.
与其他题目不同的地方是,我们维护两颗线段树,但更新是两颗线段树一起更新.
维护一个cost与一个minf,然后更新是左右找最小的cost+minf.
然后要打三个操作:
cost区间修改
f+cost区间查询.
f单点修改
区间修改要打lazy标记,然后注意每一次lazy向下推都要更新(MD因为更新的问题调了好久…)
而且,lazy标记假如打在了节点n上,要确保n当前的值是可信的,不然更新会出问题.
用一个单调队列可以O(n)计算出左边第一个比这个大的与最多可以取到左边第几个.
#include <cstdio>
#include <iostream>
#include <cstring>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define N 100010
using namespace std;
int n,f[N],a[N],b[N],lal[N],can[N],lbig[N],minn,i;
struct mqueue{
int ar[N][2],top;
void push(int loc,int v){
while (top>0 && ar[top][0]<v) lbig[ar[top--][1]]=loc;
ar[++top][0]=v;
ar[top][1]=loc;
}
mqueue() {
memset(ar,0,sizeof ar);
top=0;
}
} q;
struct node{
int f,cost,waitvalue;
bool updating;
node() {
f=cost=waitvalue=updating=0;
}
} st[10*N];
void update(int x,int l,int r) {
if (st[x*2].f+st[x*2].cost<st[x*2+1].f+st[x*2+1].cost) {
st[x].f=st[x*2].f;
st[x].cost=st[x*2].cost;
} else {
st[x].f=st[x*2+1].f;
st[x].cost=st[x*2+1].cost;
}
}
void pushupdate(int x,int l,int r) {
if (st[x].updating) {
st[x].updating=false;
st[x*2+1].cost=st[x*2].cost=st[x*2+1].waitvalue=st[x*2].waitvalue=st[x].waitvalue;
st[x*2+1].updating=st[x*2].updating=true;
}
}
void fchange(int x,int l,int r,int tg,int value) {
pushupdate(x,l,r);
if (l>tg || r<tg) return;
if (l==r) {
st[x].f=value;
return;
}
fchange(x*2,l,l+r>>1,tg,value);
fchange(x*2+1,(l+r>>1)+1,r,tg,value);
update(x,l,r);
}
void costchange(int x,int l,int r,int tl,int tr,int value) {
pushupdate(x,l,r);
if (l>tr || r<tl) return;
if (tl<=l && r<=tr) {
st[x].f=f[l];
st[x].cost=st[x].waitvalue=value;
st[x].updating=true;
pushupdate(x,l,r);
return;
}
costchange(x*2,l,l+r>>1,tl,tr,value);
costchange(x*2+1,(l+r>>1)+1,r,tl,tr,value);
update(x,l,r);
}
void ansquery(int x,int l,int r,int tl,int tr,int &rt) {
pushupdate(x,l,r);
if (l>tr || r<tl) return;
if (tl<=l && r<=tr) {
if (l!=r) update(x,l,r);
rt=min(rt,st[x].cost+st[x].f);
return;
}
ansquery(x*2,l,l+r>>1,tl,tr,rt);
ansquery(x*2+1,(l+r>>1)+1,r,tl,tr,rt);
update(x,l,r);
}
int main() {
freopen("array.in","r",stdin);
freopen("array.out","w",stdout);
cin>>n;
int maxlal=0;
for (int i=1; i<=n; i++) {
scanf("%d %d\n",&a[i],&b[i]);
maxlal=max(maxlal,lal[a[i]]+1);
can[i]=maxlal;
lal[a[i]]=i;
}
for (int i=n; i>0; i--) q.push(i,b[i]);
costchange(1,0,n,1,n,99999999);
for (int i=1; i<=n; i++) {
costchange(1,0,n,lbig[i],i-1,b[i]);
f[i]=99999999;
ansquery(1,0,n,can[i]-1,i-1,f[i]);
fchange(1,0,n,i,f[i]);
}
//for (int i=1; i<=n; i++) cout<<f[i]<<endl;
cout<<f[n]<<endl;
}