jzoj4883 灵知的太阳信仰

39 篇文章 0 订阅
28 篇文章 0 订阅

题目描述

每次核融的时候,空都会选取一些原子,排成一列。然后,她会将原子序列分成一些段,并将每段进行一次核融。
一个原子有两个属性:质子数和中子数。
每一段需要满足以下条件:
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;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值