[IOI2018] meetings 会议

https://www.luogu.org/problemnew/show/P5044

题解

这种关于最大值或者最小值的问题,可以往笛卡尔树的方面想。

先考虑一个朴素的\(dp\),设\(dp[l][r]\)表示\(l\sim r\)的答案,转移是:
\[ dp[l][r]=min(dp[l][p-1]+a[p]*(r-p+1),dp[p+1][r]+a[p]*(p-l+1)) \]

\[ p=min(a[l]\sim a[r]) \]

这个东西乍一看好像没什么优化空间,但是由于我们有笛卡尔树,所以我们可以加一些限制。

比如说我们强制让询问区间的一个端点为笛卡尔树上的某个端点。

然后考虑这时的转移:
\[ dp[l][i]=min(dp[l][p-1]+a[p]*(i-p+1),dp[p+1][i]+(p-l+1)*a[p]) \]
我们观察到随着\(i\)的向右移动,前面的部分每次都会增加\(a[p]\),后面的的部分每次增加都不会超过\(a[p]\)(根据笛卡尔树的性质)。

于是我们可以发现准备更新的这段区间被分为两部分,一部分是区间加等差数列,一部分是加常数,这个可以用线段树上二分完成。

对于一般的区间,我们直接可以把它拆成两种情况:选在最大值左边或者选在最大值右边,这样端点就有保障了,最后我们把两种情况的答案取个\(min\)就可以了。

Warning

打标记的时候要注意,因为下放的时候标记会清空,所以区间的值直接累加,不要直接更新。

代码

#include<bits/stdc++.h>
#define ls cnt<<1
#define rs cnt<<1|1
#define N 750009  
using namespace std;
typedef long long ll;
ll h[N],ans[N],ans1[N],ans2[N];
int n,lo[N],p[23][N],ch[N][2],Q,rot;
vector<int>vec1[N],id1[N],vec2[N],id2[N];
inline ll rd(){
    ll x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
inline int _max(int a,int b){return h[a]<h[b]?b:a;}
inline int f(int x){return n-x+1;}
inline int RMQ(int l,int r){
    int loo=lo[r-l+1];
    return _max(p[loo][l],p[loo][r-(1<<loo)+1]);
}
struct node{
   ll val1,val2,k,b;
   int tag;
}tr[N<<2];
inline void cover(int cnt,int l,int r,ll k,ll b){
    tr[cnt].k=k;tr[cnt].b=b;
    tr[cnt].val1=k*l+b;tr[cnt].val2=k*r+b;
    tr[cnt].tag=1;
}
inline void add(int cnt,int l,int r,ll k,ll b){ 
    tr[cnt].k+=k;tr[cnt].b+=b;
    tr[cnt].val1+=k*l+b;
    tr[cnt].val2+=k*r+b;//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    if(!tr[cnt].tag)tr[cnt].tag=2;
}
inline void pushdown(int cnt,int l,int r){
    int mid=(l+r)>>1; 
    if(tr[cnt].tag){
        if(tr[cnt].tag==1)cover(ls,l,mid,tr[cnt].k,tr[cnt].b),cover(rs,mid+1,r,tr[cnt].k,tr[cnt].b);
        else add(ls,l,mid,tr[cnt].k,tr[cnt].b),add(rs,mid+1,r,tr[cnt].k,tr[cnt].b);
        tr[cnt].k=tr[cnt].b=tr[cnt].tag=0; 
    }
}
inline void build(int &cnt,int l,int r){
    if(l>r){cnt=0;return;}
    cnt=RMQ(l,r);
    build(ch[cnt][0],l,cnt-1);
    build(ch[cnt][1],cnt+1,r);
}
ll query(int cnt,int l,int r,int x){
   if(l==r)return tr[cnt].val1;
   int mid=(l+r)>>1;
   pushdown(cnt,l,r);
   if(mid>=x)return query(ls,l,mid,x);
   else return query(rs,mid+1,r,x);
}
void clear(int cnt,int l,int r){
    tr[cnt].k=tr[cnt].val1=tr[cnt].val2=tr[cnt].b=tr[cnt].tag=0;
    if(l==r)return;
    int mid=(l+r)>>1;
    clear(ls,l,mid);clear(rs,mid+1,r);
}
void upd(int cnt,int l,int r,int x,ll y){
    if(l==r){
        add(cnt,l,r,0,y);
        return; 
    }
    pushdown(cnt,l,r);
    int mid=(l+r)>>1;
    if(mid>=x)upd(ls,l,mid,x,y);
    else upd(rs,mid+1,r,x,y);
    tr[cnt].val1=tr[ls].val1;tr[cnt].val2=tr[rs].val2;
}
void work(int cnt,int l,int r,int L,int R,ll k1,ll b1,ll b2){
    if(l>=L&&r<=R){
        if(k1*l+b1>=tr[cnt].val1+b2){   
        //   cout<<l<<" "<<r<<" "<<tr[cnt].val1<<endl;
            add(cnt,l,r,0,b2);  
            return;
        }
        if(k1*r+b1<=tr[cnt].val2+b2){
            cover(cnt,l,r,k1,b1);
            return;
        }
    }
    int mid=(l+r)>>1;
    pushdown(cnt,l,r);
    if(mid>=L)work(ls,l,mid,L,R,k1,b1,b2);
    if(mid<R)work(rs,mid+1,r,L,R,k1,b1,b2);
    tr[cnt].val1=tr[ls].val1;tr[cnt].val2=tr[rs].val2;
}
void solve(int now,int l,int r,int tag){
    ll num=h[now];
    if(ch[now][0])solve(ch[now][0],l,now-1,0),num+=query(1,1,n,now-1);
    if(ch[now][1])solve(ch[now][1],now+1,r,1);
    upd(1,1,n,now,num);
    if(now!=r){
        ll k1=h[now],b1=num-h[now]*now,b2=h[now]*(now-l+1); 
        work(1,1,n,now+1,r,k1,b1,b2);
    }
    if(tag){
        for(int i=0;i<vec1[l].size();++i){
            int x=vec1[l][i],idd=id1[l][i];
            ans[idd]=query(1,1,n,x);
        }
    }
}
int main(){
    n=rd();Q=rd();
    for(int i=1;i<=n;++i)h[i]=rd(),p[0][i]=i;
    for(int i=1;(1<<i)<=n;++i)
      for(int j=1;j+(1<<i)-1<=n;++j)p[i][j]=_max(p[i-1][j],p[i-1][j+(1<<i-1)]);
    for(int i=2;i<=n;++i)lo[i]=lo[i>>1]+1;
    for(int i=1;i<=Q;++i){
        int l=rd()+1,r=rd()+1;
        int mid=RMQ(l,r);   
        ans1[i]=1ll*(mid-l+1)*h[mid];
        if(mid+1<=r)vec1[mid+1].push_back(r),id1[mid+1].push_back(i);
        ans2[i]=1ll*(r-mid+1)*h[mid];
        if(l<=mid-1)vec2[f(mid-1)].push_back(f(l)),id2[f(mid-1)].push_back(i);
    }
    build(rot,1,n);
    solve(rot,1,n,1);
    for(int i=1;i<=Q;++i)ans1[i]+=ans[i],ans[i]=0;
    clear(1,1,n);
    reverse(h+1,h+n+1);
    for(int i=1;(1<<i)<=n;++i)
      for(int j=1;j+(1<<i)-1<=n;++j)p[i][j]=_max(p[i-1][j],p[i-1][j+(1<<i-1)]);
    for(int i=1;i<=n;++i){
      vec1[i]=vec2[i],id1[i]=id2[i];
      ch[i][0]=ch[i][1]=0;
    }
    build(rot,1,n);
    solve(rot,1,n,1);
    for(int i=1;i<=Q;++i)ans2[i]+=ans[i];
    for(int i=1;i<=Q;++i)
      printf("%lld\n",min(ans1[i],ans2[i]));
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ZH-comld/p/10808396.html