首页 > 其他分享 >Atcoder Beginner Contest ABC 275 Ex (H) Monster 题解

Atcoder Beginner Contest ABC 275 Ex (H) Monster 题解

时间:2022-11-04 16:15:01浏览次数:75  
标签:Atcoder ABC lb 题解 LL pos set 怪物 dp

先明确\(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();
}

标签:Atcoder,ABC,lb,题解,LL,pos,set,怪物,dp
From: https://www.cnblogs.com/legendstane/p/atcoder-beginner-contest-abc-275-ex-h-monster-soluti

相关文章

  • AtCoder Regular Contest 068
    C简单,D有点意思,但没啥用,不写。F有点好玩,想了一个小时,看了一上午题解,弄明白了。E-SnukeLine\(n\)个物品,第\(i\)个物品在\([l_i,r_i]\)间。你初始在第0格,......
  • 【题解】洛谷 P1134 [USACO3.2]阶乘问题
     1#include<iostream>2usingnamespacestd;34intmain(){5intn;6cin>>n;7intc=1,a=0,b=0;8for(inti=1;i......
  • LG4026 [SHOI2008]循环的债务 题解
    LG4026[SHOI2008]循环的债务给出三个整数\(x_1,x_2,x_3\)。\(x_1\)代表A欠B的钱数,\(x_2\)表示B欠C的钱数,\(x_3\)表示C欠A的钱数。接下来\(3\)行,每......
  • Sugoroku 4 (Atcoder abc275 T5) DP
    题目描述题目链接https://atcoder.jp/contests/abc275/tasks/abc275_e题意从\(0\)到\(n\)有\(n+1\)个方格,你现在在第\(0\)个格子。每次移动可以随机走\(1\)......
  • WiredTiger引擎编译 及 LT_PREREQ(2.2.6)问题解决
    近期需要为异构引擎做准备,wiredtiger以其优异的性能(B-tree和LSM-tree都支持)和稳定性(Mongodb的默认存储引擎)被我们备选为异构引擎里的一个子引擎,后续将深入wiredtiger......
  • CF 1749 题解
    比赛链接:https://codeforces.com/contest/1749题解:AB水题//bySkyRainWind#include<cstdio>#include<vector>#include<cstring>#include<iostream>#include......
  • ABC273 E~G
    E:考虑维护当前所在位置的指针。设当前点为\(u\)。对于第一个操作,我们可以将\(u\)新增一个儿子\(x\),并将指针转移到\(x\)。对于第二个操作,把指针转移到\(fa_u\)......
  • P4774 倚天屠龙传 题解
    其实这道题的主体并不难,主要是细节很多我们可以把题目分成界限分明的两部分,第一部分,屠每条龙所用的剑只和当前拥有的剑有关。于是可以单独开一个数据结构按题目维护。另......
  • 2022 CSP-S题解
    T1:假期计划给定\(n\)个点\(m\)条边的无向图,每个点有一个点权。在图中选\(4\)个不同的点,从\(1\)号点出发完成\(5\)段行程:\(1\toA\toB\toC\toD\to1\),每......
  • JOIOI の塔 题解
    题目传送门洛谷上竟然还没有题解...题目分析简单贪心题。考虑倒过来寻找。显然,如果一个J想要配成一座塔,那么必须要找一个OI。O更简单,就是直接找到一个I放上去就......