【简】题解 AWSL090429 【原子】

预处理出每个原子最近的不能合并的位置

枚举当前位置和前面断开的位置合并

发现还是不能过

考虑用选段树优化

但是因为每次转移的最优点是在前面可以合并的范围内 dp值加上当前的到该点的最大值

因为每个位置的最大值每次更新不是只更新一个位置 

是一次更新一段位置

所以直接维护复杂度爆炸

有种方法(套路) 是把最值的更新改为值的加减

因为每次是更新一段区间

且每个点到当前的位置的最值是单调不减的

所以每次的修改就可以是一段一段的

可以用单调栈来维护每种值的区间

就可以进行区间修改

#include<bits/stdc++.h> 
using namespace std;
#define ll long long
#define C getchar()-48
inline ll read()
{
    ll s=0,r=1;
    char c=C;
    for(;c<0||c>9;c=C) if(c==-3) r=-1;
    for(;c>=0&&c<=9;c=C) s=(s<<3)+(s<<1)+c;
    return s*r;
} 
const int N=1e5+10,inf=1e9; 
int n,qm;
int p[N],v[N],q[N];
int vis[N];
int dp[N];
int dv[N],dr[N],top;
struct xin{
    int del,mn;
}tr[N<<2];
inline void up(int x)
{
    tr[x].mn=min(tr[x<<1].mn,tr[x<<1|1].mn)+tr[x].del;
}
inline void down(int x)
{
    tr[x<<1].del=tr[x<<1|1].del=tr[x].del;
    tr[x].del=0;
}
inline void add(int x,int l,int r,int ql,int qr, int v)
{
    if(ql<=l&&r<=qr){tr[x].del+=v,tr[x].mn+=v;return;}
    int mid=(l+r)>>1;
    if(ql<=mid) add(x<<1,l,mid,ql,qr,v);
    if(mid<qr) add(x<<1|1,mid+1,r,ql,qr,v);
    up(x);
}
inline int ask(int x,int l,int r,int ql,int qr)
{
    if(ql<=l&&r<=qr){return tr[x].mn;}
    int mid=(l+r)>>1,mn=inf;
    if(ql<=mid) mn=min(mn,ask(x<<1,l,mid,ql,qr));
    if(mid<qr) mn=min(mn,ask(x<<1|1,mid+1,r,ql,qr));
    up(x); 
    return mn+tr[x].del;
}
int main()
{
    freopen("array.in","r",stdin); 
    freopen("array.out","w",stdout); 
    n=read();qm=n;vis[0]=1;dv[0]=inf;dr[0]=0;
    for(int i=1;i<=n;i++) p[i]=read(),v[i]=read();
    for(int i=n;i>=1;i--)
    {
        while(!vis[p[qm]]){vis[p[qm]]=1;qm--;}
        q[i]=qm;
        vis[p[i]]=0;
    }
    for(int i=1;i<=n;i++)
    {
        add(1,1,n,i,i,v[i]);
        while(dv[top]<v[i])
        {
            add(1,1,n,dr[top-1]+1,dr[top],v[i]-dv[top]);
            top--;
        }
        dv[++top]=v[i],dr[top]=i;
        dp[i]=ask(1,1,n,q[i]+1,i);
        add(1,1,n,i+1,i+1,dp[i]);
    }
    cout<<dp[n];
    return 0;
}

还有位大佬用multiset维护

#include<bits/stdc++.h>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=100000,INF=(1<<30)-1;

multiset<int>t;
int n,a[N+9],b[N+9],data[N+9],last[N+9],l[N+9];
int sum[N+9],wei[N+9],f[N+9],hd,tl;

Abigail into(){
  scanf("%d",&n);
  for (int i=1;i<=n;++i)
    scanf("%d%d",&a[i],&b[i]);
}

Abigail work(){
  for (int i=1;i<=n;++i)
    l[i]=max(l[i-1],last[a[i]]+1),last[a[i]]=i;
  //预处理每一个位置为结尾可以取的转移的区间左端点 
  hd=1;
  int k;
  for (int i=1;i<=n;++i){
      k=i-1;
      for (;hd<tl&&wei[hd+1]<l[i];++hd)
        t.erase(t.find(sum[hd]));
      //把不在转移区间的位置去掉 
      for (;hd<=tl&&b[i]>data[tl];--tl)
        t.erase(t.find(sum[tl])),k=wei[tl];
      //更新阶梯型(max{b[j]..b[i]})
      data[++tl]=b[i];
      wei[tl]=k;
      //加入第i个位置 
      if (hd^tl){
        sum[tl]=f[wei[tl]]+data[tl];
        //把位置i所在的阶梯中最优的值sum[tl]计算出来 
        t.insert(sum[tl]);
        //加入set中 
        t.erase(t.find(sum[hd]));
        //开头的那一段阶梯中会有一部分不可取,一部分可取 
      }
      sum[hd]=f[l[i]-1]+data[hd];
      //采取开头最优的那段 
      t.insert(sum[hd]);
      //加入set中 
      f[i]=*t.begin();
  }
}

Abigail outo(){
  printf("%d\n",f[n]);
}

int main(){
  freopen("array.in","r",stdin);
  freopen("array.out","w",stdout);
  into();
  work();
  outo();
  return 0;
}
posted @ 2019-05-03 20:12  1436177712  阅读(188)  评论(0编辑  收藏  举报