zoukankan      html  css  js  c++  java
  • [IOI2018] meetings 会议

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

    题解

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

    先考虑一个朴素的(dp),设(dp[l][r])表示(lsim 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
    ",min(ans1[i],ans2[i]));
    	return 0;
    }
    
  • 相关阅读:
    xsos:一个在Linux上阅读SOSReport的工具
    RHEL sosreport
    sosreport -a --report
    环境变量
    读研重要的是要明白你自己要干什么, 不能等导师来告诉你你应该干什么. 研究生的优势在于理论功底深厚, 思维具有穿透力,
    awk sed grep 常用命令
    如何删除文件中的空行
    Vim删除空行
    WPS 2010 页眉下方添加下划线
    Android开发环境搭建
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/10808396.html
Copyright © 2011-2022 走看看