先明确\(a_i\)是一个怪物的血量,\(b_i\)是攻击的代价。发现如果我们想攻击一个怪物,不如找出一个极大的包含它的区间,满足这个区间内所有怪物的攻击代价都不大于它本身的代价,因为多攻击一点肯定是好的,反正不多花钱。这启发我们把所有的怪物依照\(b_i\)的值建一棵笛卡尔树,其中\(b_i\)最大的怪物为根。容易看出一定存在一个最优解,满足每次攻击都是攻击了笛卡尔树上的一个点以及它子树中所有的怪物。我们把对一个点x及其子树内所有点的攻击称为”位于x的攻击”。

\(dp_{i,j}\)表示已经做完了所有位于i及其子树内的攻击,子树中剩余血量最大的怪物的血量\(\le j\)时的最小代价。转移的时候先把当前点的dp值与儿子的dp值合并,即\(dp_{i,j}+=dp_{k,j}\),其中k为i的儿子。接下来考虑位于当前点的怪物,根据定义把满足\(j<a_i\)\(dp_{i,j}\)都设为无穷大。最后考虑进行一些位于i的攻击,也就是对于所有j,进行\(dp_{i,j-1} \leftarrow dp_{i,j}+b_i\)的更新操作;为了满足dp数组不增的性质(因为dp的定义是血量\(\le j\)的代价),应该从大往小枚举j。但是j的值域很大,这样是没法过的。

固定i,把每个点对\((j,dp_{i,j})\)都画在坐标系上,发现这些点构成一个下凸函数。证明可以使用归纳法,首先每个叶子结点的dp数组肯定是下凸的,长这样:

非叶节点转移的时候,先合并dp值,众所周知几个下凸函数合并(每个点分别加)后仍是下凸的。最后更新的时候,其实是把图像最左侧几段斜率小于\(-b_i\)的改成了斜率为\(-b_i\)的:

我们可以用set来维护斜率的从右到左的变化。在图像的最右段,斜率和函数值都是0。我们在每个点的set中维护一些点对\((x,y)\),表示从右往左看,在横坐标为x的位置,函数图像的斜率增加了y。这样的好处是合并dp值很好做,只要把子树的set启发式合并就可以了,如果有横坐标重复的则把对应增加的值相加。在维护set的同时记录每个图像在x=0处的斜率,这样在考虑当前点的怪物以及最后更新时,可以直接在set的最前端erase掉斜率不合格的部分。
时间复杂度\(O(nlog^2n)\)

点击查看代码
#include <bits/stdc++.h>

#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <LL,LL>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back

void fileio()
{
  #ifdef LGSs
  freopen("in.txt","r",stdin);
  freopen("out.txt","w",stdout);
  #endif
}
void termin()
{
  #ifdef LGSs
  std::cout<<"\n\nPROGRAM TERMINATED";
  #endif
  exit(0);
}

using namespace std;

const LL inf=2e9;

LL n,a[100010],b[100010],root,slp0[100010];
vector <LL> g[100010];
set <pii> f[100010];

namespace st
{
  LL n2=1;
  pii dat[400010];
  void build(LL k,LL lb,LL ub)
  {
    if(lb==ub) return;
    build(k+k+1,lb,(lb+ub)/2);build(k+k+2,(lb+ub)/2+1,ub);
    dat[k]=max(dat[k+k+1],dat[k+k+2]);
  }
  pii qry(LL k,LL lb,LL ub,LL tlb,LL tub)
  {
    if(ub<tlb||tub<lb) return mpr(0LL,0LL);
    if(tlb<=lb&&ub<=tub) return dat[k];
    return max(qry(k+k+1,lb,(lb+ub)/2,tlb,tub),qry(k+k+2,(lb+ub)/2+1,ub,tlb,tub));
  }
}

LL buildTree(LL lb,LL ub)
{
  pii val=st::qry(0,0,st::n2-1,lb,ub);
  LL ret=val.se;
  if(lb<ret) g[ret].pb(buildTree(lb,ret-1));
  if(ret<ub) g[ret].pb(buildTree(ret+1,ub));
  return ret;
}

void dfs(LL pos)
{
  if(g[pos].empty())
  {
    slp0[pos]=b[pos];
    f[pos].insert(mpr(inf,0));f[pos].insert(mpr(a[pos],b[pos]));
    return;
  }
  rep(i,g[pos].size())
  {
    LL u=g[pos][i];
    dfs(u);
    slp0[pos]+=slp0[u];
    if(f[pos].size()<f[u].size()) swap(f[pos],f[u]);
    for(auto it:f[u])
    {
      auto itt=f[pos].lower_bound(mpr(it.fi,0LL));
      if(itt!=f[pos].end()&&itt->fi==it.fi)
      {
        LL val=itt->se+it.se;
        f[pos].erase(itt);
        f[pos].insert(mpr(it.fi,val));
      }
      else f[pos].insert(it);
    }
  }
  while(f[pos].begin()->fi<=a[pos]) slp0[pos]-=f[pos].begin()->se,f[pos].erase(f[pos].begin());
  f[pos].insert(mpr(a[pos],inf));slp0[pos]+=inf;
  while(true)
  {
    //if(f[pos].empty()) cout<<"FUCK",exit(0);
    LL afterSlp=slp0[pos]-f[pos].begin()->se;
    if(afterSlp<b[pos])
    {
      LL act=b[pos]-afterSlp,x=f[pos].begin()->fi;
      f[pos].erase(f[pos].begin());f[pos].insert(mpr(x,act));
      slp0[pos]=b[pos];
      break;
    }
    slp0[pos]=afterSlp;
    f[pos].erase(f[pos].begin());
  }
}

int main()
{
  fileio();

  cin>>n;
  rep(i,n) scanf("%lld",&a[i]);
  rep(i,n) scanf("%lld",&b[i]);
  while(st::n2<n) st::n2*=2;
  rep(i,n) st::dat[i+st::n2-1]=mpr(b[i],i);
  st::build(0,0,st::n2-1);
  root=buildTree(0,n-1);
  dfs(root);
  LL slp=0,ans=0;
  for(auto it=--f[root].end();;--it)
  {
    slp+=it->se;
    if(it==f[root].begin()) ans+=slp*it->fi;
    else
    {
      auto itt=it;--itt;
      ans+=slp*(it->fi-itt->fi);
    }
    if(it==f[root].begin()) break;
  }
  cout<<ans<<endl;

  termin();
}

原文地址:http://www.cnblogs.com/legendstane/p/atcoder-beginner-contest-abc-275-ex-h-monster-solution.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性