题解 寄
平凡树形 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;
}