zoukankan      html  css  js  c++  java
  • 「JOISC 2020 Day3」星座 3 (dp)

    「JOISC 2020 Day3」星座 3 (dp)

    考虑根据(A_i)的值建立笛卡尔树,此时平面被划分为个矩形空间

    下称选择一个点为保留一个星星

    Snipaste_2021-03-13_11-21-27.png

    具体的,对于笛卡尔树上的节点((u,l,r)),它的矩形就是父节点矩形以下,且满足(xin[l,r],y>A_u)的部分

    可以用一个线段树来查询矩形内部的点,线段树上每个节点维护(y_{max}),每次剥掉(y_{max}>A_u)的部分

    复杂度为均摊(O(nlog n))

    [ ]

    观察笛卡尔树的树形,容易发现,

    1.笛卡尔树左右子树的矩形之间不会产生贡献

    2.每个节点对应的矩形区间内最多选择一个点

    3.如果一个节点((u,l,r))的祖先中有一个(x_iin[l,r])的点选择了,那么自己的矩形内不能选择点

    那么令(dp_{u,i})表示父节点传下来的点(x=i)时,(u)子树内的答案

    对于(iin[l,r])的情况,可以直接将儿子的值合并,加上自己区间内部的权值总和(C_i)

    对于(i ot in[l,r])的情况,这一部分答案相同

    可以从自己子区间内选择一个点((x_i,y_i,c_i))下传,此时沿用上面合并得到的(dp)

    (outans=min{dp_{x_i}+sum-c_i})

    如何实现这个奇怪的(dp)过程?

    考虑子树的区间不交,因此对于((u,l,r)),只维护(l,r)内部的答案,对于(i ot in[l,r])的部分额外记录一个值(dp_u)

    考虑用一棵静态的线段树维护(dp),线段树上存储(iin[l,r])的答案

    合并左右儿子时,两个儿子的区间不交

    因此,实际上答案就是将(dp_{ls})加到([u,r])上,将(dp_{rs})加到([l,u])

    处理出(sum)之后,区间修改([l,r])的答案,对于(dp_u)直接按照上面的方法枚举((x_i,y_i,c_i))来计算即可

    复杂度为(O(nlog n))

    int n,A[N];
    struct SegFinder{
    	vector <Pii> V[N];
    	int s[N<<2];
    	void Build(int p,int l,int r){
    		if(l==r) {
    			sort(V[l].begin(),V[l].end());
    			s[p]=V[l].empty()?0:V[l].back().first;
    			return;
    		}
    		int mid=(l+r)>>1;
    		Build(p<<1,l,mid),Build(p<<1|1,mid+1,r);
    		s[p]=max(s[p<<1],s[p<<1|1]);
    	}
    	void Get(int p,int l,int r,int ql,int qr,int x,vector <Pii> &Res){
    		if(s[p]<x) return;
    		if(l==r) {
    			while(!V[l].empty() && V[l].back().first>=x) Res.pb(mp(l,V[l].back().second)),V[l].pop_back();
    			s[p]=V[l].empty()?0:V[l].back().first;
    			return;
    		}
    		int mid=(l+r)>>1;
    		if(ql<=mid) Get(p<<1,l,mid,ql,qr,x,Res);
    		if(qr>mid) Get(p<<1|1,mid+1,r,ql,qr,x,Res);
    		s[p]=max(s[p<<1],s[p<<1|1]);
    	}
    } Finder;
    
    int ls[N],rs[N],stk[N],top,mk[N];
    int rt[N];
    ll dp[N],s[N<<2],t[N<<2];
    ll Que(int p,int l,int r,int ql,int qr){
    	if(ql<=l && r<=qr) return s[p];
    	int mid=(l+r)>>1; ll res=1e18;
    	if(ql<=mid) cmin(res,Que(p<<1,l,mid,ql,qr));
    	if(qr>mid) cmin(res,Que(p<<1|1,mid+1,r,ql,qr));
    	return res+t[p];
    }
    
    void Upd(int p,int l,int r,int ql,int qr,ll x){
    	if(ql<=l && r<=qr) {
    		s[p]+=x,t[p]+=x;
    		return;
    	}
    	int mid=(l+r)>>1;
    	if(ql<=mid) Upd(p<<1,l,mid,ql,qr,x);
    	if(qr>mid) Upd(p<<1|1,mid+1,r,ql,qr,x);
    	s[p]=min(s[p<<1],s[p<<1|1])+t[p];
    }
    
    // 线段树内存储的是 如果父节点有下传下来的答案
    // dp 存储没有父节点下传的答案
    
    void Solve(int p,int l,int r){
    	vector <Pii> V; Finder.Get(1,1,n,l,r,A[p]+1,V);
    	// 拿出我的决策矩形
    
    	if(l<p) Solve(ls[p],l,p-1);
    	if(p<r) Solve(rs[p],p+1,r);
    	if(rs[p]) Upd(1,1,n,l,p,dp[rs[p]]);
    	if(ls[p]) Upd(1,1,n,p,r,dp[ls[p]]);
    	ll sum=0;
    	for(Pii i:V) sum+=i.second;
    	if(sum) Upd(1,1,n,l,r,sum); 
    	// 如果父节点有下传,那么自己必须被清空
    	// 否则考虑选择一个下传下去,这样就能得到 没有父节点下传时的值
    	dp[p]=Que(1,1,n,l,r);
    	for(Pii i:V) cmin(dp[p],Que(1,1,n,i.first,i.first)-i.second);
    }
    
    int main(){
    	n=rd();
    	rep(i,1,n) {
    		A[i]=rd();
    		while(top && A[stk[top]]<=A[i]) ls[i]=stk[top--];
    		stk[++top]=i;
    	}
    	top=0;
    	drep(i,n,1) {
    		while(top && A[stk[top]]<A[i]) rs[i]=stk[top--];
    		stk[++top]=i;
    	}
    	rep(i,1,n) mk[ls[i]]=mk[rs[i]]=1;
    	rep(_,1,rd()) {
    		int x=rd(),y=rd(),c=rd();
    		Finder.V[x].pb(mp(y,c));
    	}
    	Finder.Build(1,1,n);
    	rep(i,1,n) if(!mk[i]) {
    		Solve(i,1,n);
    		printf("%lld
    ",dp[i]);
    		return 0;
    	}
    }
    
  • 相关阅读:
    java并发包——Semaphore(信号量)
    cnblogs
    linux下安装python3及对应的pip环境
    Swing实现AES加密&解密工具升级
    Netty——知识点总结
    RPC——看这一篇就…显然不够
    ORM——Mybatis
    com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException问题解决
    设计模式分类及常用设计模式
    Java 语言特性【四】——其他
  • 原文地址:https://www.cnblogs.com/chasedeath/p/14528225.html
Copyright © 2011-2022 走看看