题解 寄

传送门

平凡树形 DP 已经做不出来了,怎么办啊

发现需要记录的信息真的很多,光记录到定义里面维数就爆表了
但是发现真正有用的东西可以分为独立的两部分(这两部分是互相转移的关系)
于是令 \(f_{i, j}\) 为在点 \(i\) 子树内,有 \(j\) 个点还未分配管辖点(但统计了这些点到 \(i\) 的距离)的最小代价
\(g_{i, j}\)\(i\) 子树内的关键点均已分配管辖点,子树内离 \(i\) 最近的管辖点为 \(j\) 的最小代价
然后这两个按照定义互相转移即可
复杂度 \(O(n^2)\)
貌似还有一种做法,感觉更自然一点的样子

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3f
#define N 3010
#define pb push_back
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;
ll c;
bool iskey[N];
vector<int> key;
int head[N], cnt[N], ecnt;
struct edge{int to, next; ll val;}e[N<<1];
inline void add(int s, int t, ll w) {e[++ecnt]={t, head[s], w}; head[s]=ecnt;}

namespace force{
	int sta[N], top;
	ll dis[50][50], ans=INF;
	void dfs(int u, int fa, ll now, ll* dis) {
		dis[u]=now;
		for (int i=head[u],v; ~i; i=e[i].next) {
			v = e[i].to;
			if (v==fa) continue;
			dfs(v, u, now+e[i].val, dis);
		}
	}
	void solve() {
		for (int i=1; i<=n; ++i) dfs(i, 0, 0, dis[i]);
		int lim=1<<n;
		for (int s=1; s<lim; ++s) {
			ll sum=c*__builtin_popcount(s); top=0;
			if (sum>ans) continue;
			for (int i=1; i<=n; ++i) if (s&(1<<i-1)) sta[++top]=i;
			for (auto& it:key) {
				ll minn=INF;
				for (int i=1; i<=top; ++i) minn=min(minn, dis[it][sta[i]]);
				sum+=minn;
			}
			ans=min(ans, sum);
		}
		printf("%lld\n", ans);
	}
}

namespace task{
	int siz[N];
	vector<int> sub[N];
	ll f[N][N], g[N][N], dis[N][N];
	void dfs(int u, int fa, ll now, ll* dis) {
		dis[u]=now;
		for (int i=head[u],v; ~i; i=e[i].next) {
			v = e[i].to;
			if (v==fa) continue;
			dfs(v, u, now+e[i].val, dis);
		}
	}
	void dfs(int u, int fa) {
		ll tem[N];
		f[u][cnt[u]]=0; g[u][u]=c;
		siz[u]=cnt[u]; sub[u].pb(u);
		for (int i=head[u],v; ~i; i=e[i].next) {
			v = e[i].to;
			if (v==fa) continue;
			dfs(v, u);
			// cout<<"merge: "<<u<<' '<<v<<endl;
			memset(tem, 0x3f, sizeof(tem));
			for (int j=0; j<=siz[u]; ++j)
				for (auto k:sub[v])
					tem[k]=min(tem[k], f[u][j]+g[v][k]+j*dis[u][k]); //, cout<<"tr1 - j&k: "<<j<<' '<<k<<endl;
			for (int j=0; j<=siz[v]; ++j)
				for (auto k:sub[u])
					tem[k]=min(tem[k], f[v][j]+g[u][k]+j*dis[v][k]);
			memcpy(g[u], tem, sizeof(tem));
			memset(tem, 0x3f, sizeof(tem));
			for (int j=0; j<=siz[u]; ++j)
				for (int k=0; k<=siz[v]; ++k)
					tem[j+k]=min(tem[j+k], f[u][j]+f[v][k]+k*e[i].val);
			memcpy(f[u], tem, sizeof(tem));
			siz[u]+=siz[v];
			for (auto it:sub[v]) sub[u].pb(it);
			for (auto it:sub[u]) f[u][0]=min(f[u][0], g[u][it]);
			// cout<<u<<"f after merge: "; for (int j=0; j<=n; ++j) cout<<f[u][j]<<' '; cout<<endl;
			// cout<<u<<"g after merge: "; for (int j=1; j<=n; ++j) cout<<g[u][j]<<' '; cout<<endl;
		}
		for (auto it:sub[u]) f[u][0]=min(f[u][0], g[u][it]);
	}
	void solve() {
		memset(f, 0x3f, sizeof(f));
		memset(g, 0x3f, sizeof(g));
		for (int i=1; i<=n; ++i) dfs(i, 0, 0, dis[i]);
		dfs(1, 0);
		// cout<<"---f---"<<endl; for (int i=1; i<=n; ++i) {cout<<i<<": "; for (int j=0; j<=n; ++j) cout<<f[i][j]<<' '; cout<<endl;}
		// cout<<"---g---"<<endl; for (int i=1; i<=n; ++i) {cout<<i<<": "; for (int j=1; j<=n; ++j) cout<<g[i][j]<<' '; cout<<endl;}
		printf("%lld\n", f[1][0]);
	}
}

signed main()
{
	freopen("post.in", "r", stdin);
	freopen("post.out", "w", stdout);

	n=read(); m=read(); c=read();
	memset(head, -1, sizeof(head));
	for (int i=1,u,v,w; i<n; ++i) {
		u=read(); v=read(); w=read();
		add(u, v, w); add(v, u, w);
	}
	for (int i=1,x; i<=m; ++i) {
		iskey[x=read()]=1;
		key.pb(x);
		++cnt[x];
	}
	// force::solve();
	task::solve();

	return 0;
}
posted @ 2022-06-25 07:13  Administrator-09  阅读(1)  评论(0编辑  收藏  举报