掌中之物,未必在掌握之中。|

2021hych

园龄:2年7个月粉丝:2关注:2

P9550 「PHOI-1」晚宴筵题解

image

题解

简化一下题意,已知从 (p,q) 直接到达 (x,y) 的费用函数如下:

cost(p,q,x,y)={wp+wq+wx+wypqxy,l1xpr1x,l2yqr2yinf,otherwise

问从 (1,1) 到各位置的最小费用。

如果暴力建图跑最短路,由于 n 可以达到 1e3,一共有 n2 个点,边更多,肯定爆炸。

不妨思考是否有 DP 的做法,如果设 dpi,j 表示 (1,1)(i,j) 的最小费用,则有:

dpx,y=minl1xpr1x,l2yqr2y(dpp,q+cost(p,q,x,y))

注意到,r1x<x,r2y<y,显然满足无后效性,可以 DP,但是暴力是 n4 的,仍然超时。

将方程抽象成矩阵问题,有一个 (n+1)×(n+1) 的矩阵,因为有 0。每个位置 (i,j) 有一个权值表示 dpi,j+wi+wjij。那么在求解 (x,y) 时,相当于在一个子矩阵中求出最小权值 Pre,然后让 dpx,y=Pre+wx+wyxy,接着修改 (x,y) 处的数值为 dpx,y+wx+wyxy

也就是要维护子矩阵极值,单点修改。我们可以用 1 个线段树维护行,线段树的每个结点上维护一个线段树来维护列。即线段树套线段树,容易证明,单次操作是 O(log2n) 的。总体的时间复杂度就是 O(n2log2n) 可以接受。

代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
const int inf=1e9;
int n,l1[N],r1[N],l2[N],r2[N],w[N]; 
int dp[N][N],Pre;
struct node_y {
	int l,r,val;
};
struct tree_y {
	node_y tr[N<<2];
	void pushup(int p) {
		tr[p].val=min(tr[p<<1].val,tr[p<<1|1].val);
	}
	void build(int p,int l,int r) {
		tr[p].l=l,tr[p].r=r;
		if(l==r) {
			tr[p].val=inf;
			return;
		}
		int mid=l+r>>1;
		build(p<<1,l,mid);
		build(p<<1|1,mid+1,r);
		pushup(p);
	}
	void update(int p,int y,int k) {
		if(tr[p].l==tr[p].r) {
			tr[p].val=min(tr[p].val,k);
			return;
		}
		int mid=(tr[p].l+tr[p].r)>>1;
		if(y<=mid) update(p<<1,y,k);
		else update(p<<1|1,y,k);
		pushup(p);
	}
	int query(int p,int l,int r) {
		if(l<=tr[p].l&&tr[p].r<=r) return tr[p].val;
		int mid=(tr[p].l+tr[p].r)>>1;
		int val=inf;
		if(l<=mid) val=min(val,query(p<<1,l,r));
		if(r>mid) val=min(val,query(p<<1|1,l,r));
		return val;
	} 
};
struct node_x {
	int l,r;
	tree_y T;
};
struct tree_x {
	node_x tr[N<<2];
	void build(int p,int l,int r) {
		tr[p].l=l,tr[p].r=r;
		tr[p].T.build(1,1,n+1);
		if(tr[p].l==tr[p].r) return;
		int mid=l+r>>1;
		build(p<<1,l,mid);
		build(p<<1|1,mid+1,r);
	}
	void update(int p,int x,int y,int k) {
		tr[p].T.update(1,y,k);
		if(tr[p].l==tr[p].r) return;
		int mid=(tr[p].l+tr[p].r)>>1;
		if(x<=mid) update(p<<1,x,y,k);
		else update(p<<1|1,x,y,k);
	}
	int query(int p,int lx,int rx,int ly,int ry) {
		if(lx<=tr[p].l&&tr[p].r<=rx) return tr[p].T.query(1,ly,ry);
		int mid=(tr[p].l+tr[p].r)>>1;
		int val=inf;
		if(lx<=mid) val=min(val,query(p<<1,lx,rx,ly,ry));
		if(rx>mid) val=min(val,query(p<<1|1,lx,rx,ly,ry));
		return val;
	}
};
tree_x A;
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>l1[i];
	for(int i=1;i<=n;i++) cin>>r1[i];
	for(int i=1;i<=n;i++) cin>>l2[i];
	for(int i=1;i<=n;i++) cin>>r2[i];
	for(int i=1;i<=n;i++) cin>>w[i];
	A.build(1,1,n+1);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++) dp[i][j]=inf;
	dp[1][1]=0;
	A.update(1,2,2,2*w[1]-2);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++) {
			if(i==1&&j==1) continue;
			Pre=inf;
			Pre=min(Pre,A.query(1,l1[i]+1,r1[i]+1,l2[j]+1,r2[j]+1));
			if(Pre!=inf) {
				dp[i][j]=Pre+w[i]+w[j]-i-j;
				A.update(1,i+1,j+1,dp[i][j]+w[i]+w[j]-i-j);
			}
		}
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=n;j++) {
			if(dp[i][j]==inf) cout<<"inf ";
			else cout<<dp[i][j]<<" ";
		}
		cout<<endl;
	}
	return 0;
}

本文作者:2021hych

本文链接:https://www.cnblogs.com/2021hych/p/17990965

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   2021hych  阅读(15)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起