CF 70 E 题解

d p i , j dp_{i,j} dpi,j表示距离i最近的标记点为j。那么:
d p i , j = { . . . + k ( i = j ) . . . + d d i s i , j ( i ≠ j ) dp_{i,j}=\left\{\begin{matrix} ...+k(i=j) \\ ...+d_{dis_{i,j}}(i\neq j) \end{matrix}\right. dpi,j={...+k(i=j)...+ddisi,j(i=j)
那么这个省略号里面是什么呢?我们只知道是转移到子树的dp。
我们可以分情况讨论:
如果 d e p t h ( i ) > d e p t h ( j ) depth(i)>depth(j) depth(i)>depth(j)的时候则对于儿子u的转移如下:
d p i , j = ( ∑ m i n ( m i n { d p u , m } , d p u , j ) ) ( u ∈ s o n ( i ) , m ∈ { c h i l d r e n ( u ) , u } ) + . . . ( 最 开 头 的 那 两 个 ) dp_{i,j}=(\sum min(min\{dp_{u,m}\},dp_{u,j}))(u\in son(i),m\in \{children(u),u\})+...(最开头的那两个) dpi,j=(min(min{dpu,m},dpu,j))(uson(i),m{children(u),u})+...()
但是其实隐藏了一个条件就是: d i s u , m + 1 ≥ d i s i , j , 但 是 不 然 的 话 d i s i , j 最 小 就 不 满 足 dis_{u,m}+1\geq dis_{i,j},但是不然的话dis_{i,j}最小就不满足 disu,m+1disi,jdisi,j,不过这种情况会被 d p i , m dp_{i,m} dpi,m所覆盖,答案会更优所以就没关系了。
接下来考虑 d e p t h ( i ) < d e p t h ( j ) depth(i)<depth(j) depth(i)<depth(j)的情况:
d p i , j = ( . . . ) + ∑ { d p u , j ( u 在 i 到 j 的 路 径 上 ) ( m i n ( m i n { d p u , m } , d p u , j ) ) ( u ∈ s o n ( i ) , m ∈ { c h i l d r e n ( u ) , u } ) dp_{i,j}=(...)+\sum \left\{\begin{matrix} dp_{u,j}(u在i到j的路径上)\\ (min(min\{dp_{u,m}\},dp_{u,j}))(u\in son(i),m\in \{children(u),u\}) \end{matrix}\right. dpi,j=(...)+{dpu,j(uij)(min(min{dpu,m},dpu,j))(uson(i),m{children(u),u})
其实 m i n { d p u , m } min\{dp_{u,m}\} min{dpu,m}这个东西是可以预处理的,所以总的复杂度是 O ( n 2 ) O(n^2) O(n2)的,这题的n其实可以出到 5000 5000 5000
输出路径的话其实只需要跑一个和原来一样的记忆话搜索就ok了。
code:

/*
{

AuThOr Gwj
*/
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
int n,k;
const int MAXN=180+2;
int dp2[MAXN],dp[MAXN][MAXN],d[MAXN],depth[MAXN],dis[MAXN][MAXN];
bool vis[MAXN][MAXN];
vector<int> g[MAXN];
int dfs(int now,int best,int pre=0){
	if(vis[now][best]){
		return dp[now][best];
	}
	vis[now][best]=1;
	if(best==now){
		dp[now][best]=k;
	}
	else{
		dp[now][best]=d[dis[now][best]];
	}
	for(auto it:g[now]){
		if(it==pre) continue;
		if(dis[it][best]+1==dis[now][best]) continue; 
		dp[now][best]+=min(dfs(it,best,now),dp2[it]);
	} 
	for(auto it:g[now]){
		//cout<<now<<" "<<best<<" "<<it<<" "<<dis[it][best]<<" "<<dis[now][best]<<" "<<pre<<endl;
		if(it==pre) continue;
		if(dis[it][best]+1==dis[now][best]){
//			cout<<now<<"Yes"<<best<<" "<<it<<endl;
			dp[now][best]+=dfs(it,best,now);	
		} 
	}
	return dp[now][best];
}
int run(int st,int now,int pre=0){
//	cout<<<<now<<"~"<<pre<<endl;
	int res=dfs(st,now,pre);
	for(auto it:g[now]){
		if(depth[it]>depth[now])
		res=min(res,run(st,it,pre));
	} 
	return res;
}
void init(int now,int pre=0){
	depth[now]=depth[pre]+1;
	for(auto it:g[now]){
		if(it==pre) continue;
		init(it,now);
	}
}
void dfs_dis(int st,int now,int pre=0,int dist=0){
	dis[st][now]=dist;
	for(auto it:g[now]){
		if(it==pre) continue;
		dfs_dis(st,it,now,dist+1);
	} 
}
void dfs_help(int now,int pre=0){
	for(auto it:g[now]){
		if(it==pre) continue;
		dfs_help(it,now);
	}
	dp2[now]=run(now,now,pre);
}
int rest[MAXN];
int print2(int now,int pre){
	rb(i,1,n){
		if(depth[i]>=depth[now]&&dis[i][now]==depth[i]-depth[now]&&dp2[now]==dfs(now,i,pre)){
			return i;
		}
	}
	assert(0);
	return now;//That's impossible
} 
void print(int now,int best,int pre){
	rest[now]=best;
	for(auto it:g[now]){
		if(it==pre) continue;
		if(dis[it][best]+1==dis[now][best]) continue; 
		if(dfs(it,best,now)<dp2[it]){
			print(it,best,now);
		}
		else{
			print(it,print2(it,now),now);
		}
	} 
	for(auto it:g[now]){
		if(it==pre) continue;
		if(dis[it][best]+1==dis[now][best]){
			print(it,best,now);
			break;
		}
	}
}
int main(){
	fastio;
	R2(n,k);
	rb(i,1,n-1){
		R(d[i]);
	}
	rb(i,2,n){
		int u,v;
		R2(u,v);
		g[u].PB(v);
		g[v].PB(u);
	}
	memset(dp2,63,sizeof(dp2));
	init(1);
	rb(i,1,n)
		dfs_dis(i,i);
	dfs_help(1);
	int res=INF;
	rb(i,1,n)
	{
		res=min(dfs(1,i),res);
	}
	cout<<res<<endl;
	rb(i,1,n){
		if(dfs(1,i)==res){
			print(1,i,0);
			break;
		}
	} 
	rb(i,1,n){
		cout<<rest[i]<<" ";
	}cout<<endl;
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值