G. Forbidden Value 动态开点线段树

27 篇文章 0 订阅

题目链接

思路:考虑 d p i j dp_{ij} dpij表示在一个 i f if if框内,执行到第 i i i次操作时,获得值为 j j j的最小花费。初始只有进入该 i f if if时的初值有一个代价,其余都是无穷大。
那么如果时 s e t    y    v set ~~y~~v set  y  v的话,任意不等于 y y y的值都必须花费 v v v的代价来跳过这一次操作从而不成为 y y y,而 y y y可以从其他任意值转移过来(注意不能等于 x x x)。那么这样就可以用一个线段树来维护一下全局最小值,另外需要支持单点查询。维护完所有的操作后,就能知道每个值的最小代价了。
这是一个 i f if if框的情况,对于多个 i f if if,显然不能每个都开一个线段树,但是我们注意到,每个 i f if if框内有效的数的总和是 N N N级别的。那么就可以对每个 i f if if框动态建一个线段树来维护最小值。然后嵌套的框就合并起来。
合并的时候注意如果有一个为 n u l l null null就直接返回另一个不然复杂度就不对了。

细节见代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
#define fi first
#define se second
#define pb push_back
#define mid (l+r>>1)
#define wzh(x) cerr<<#x<<'='<<x<<endl;
int n,s;
const LL MX=1e18;
struct uzi{
  LL mn,tag;
  uzi *ls,*rs;
};
uzi * new_node(){
  auto tmp=new uzi;
  tmp->mn=MX;
  tmp->tag=0;
  tmp->ls=tmp->rs=NULL;
  return tmp;
}
void push_up(uzi * o){
  LL res=MX;
  if(o->ls!=NULL)res=min(res,o->ls->mn);
  if(o->rs!=NULL)res=min(res,o->rs->mn);
  o->mn=res;
}
void add(uzi *o,LL val){
  o->tag+=val;
  o->mn+=val;
}
void push_down(uzi *o){
  if(o->tag){
    if(o->ls!=NULL)add(o->ls,o->tag);
    if(o->rs!=NULL)add(o->rs,o->tag);
    o->tag=0;
  }
}
void up(uzi * &o,int l,int r,int x,LL y){
  if(o==NULL)o=new_node();
  if(l==r){
    o->mn=y;
    return;
  }
  push_down(o);
  if(x<=mid)up(o->ls,l,mid,x,y);
  else up(o->rs,mid+1,r,x,y);
  push_up(o);
}
LL get(uzi *o,int l,int r,int x){
  if(o==NULL)return MX;
  if(l==r){
    return o->mn;
  }
  push_down(o);
  if(x<=mid)return get(o->ls,l,mid,x);
  else return get(o->rs,mid+1,r,x);
}
uzi * merge(uzi *x,uzi *y){
  if(x==NULL)return y;
  if(y==NULL)return x;
  x->mn=min(x->mn,y->mn);
  push_down(x);
  push_down(y);
  x->ls=merge(x->ls,y->ls);
  x->rs=merge(x->rs,y->rs);
  return x;
}
string S;
uzi * solve(int now,LL val){
  uzi * tmp=new_node();
  up(tmp,0,N,now,val);
  int y,v;
  while(cin>>S){
    if(S[0]=='s'){
      cin>>y>>v;
      LL now_min=tmp->mn;
      tmp->tag+=v;
      tmp->mn+=v;
      if(y!=s)up(tmp,0,N,y,now_min);
    }else if(S[0]=='i'){
      cin>>y;
      auto res=solve(y,get(tmp,0,N,y));
      up(tmp,0,N,y,MX);//把y的值给清掉,因为内层if已经更新了y的值。
      tmp=merge(tmp,res);
    }else{
      return tmp;
    }
  }
  return tmp;
}
int main() {
  ios::sync_with_stdio(false);
  cin>>n>>s;
  cout<<solve(0,0)->mn<<'\n';
  return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值