//给你个树,序列,分隔 序列k段, 求 每段lca 深度之和最小。
/*
求[i,j]的LCA 我们可以通过求[i,j-1] and j的LCA ;
deep(lca) <=deep[i,j];
deep(lca) =min(deep(i,j-1),lca(j,j-1);
我们可以通过 lca(pair(i,i+1)) 计算出所有区间的lca;
假设 区间 3,4,5,6;
lca = min{lca(3,4)....lca(5,6)}
假设区间 3,4,5,6,7,8 最优解分成了两端, 3,4为一段答案, 7,8 为另一段答案;
那显然 lca(3,4),lca(7,8)<=lca(4,5),lca(5,6),lca(6,7)
5,6,分给那个区间都没有影响
所以可以 假设每次分配时,都以当前位置作为分隔点,进行状态转移。
dp[i][j] 前i 个 分了 j 段
dp[i][j] =min{dp[i][j-1] ,dp[i-1][j-1]+deep(i),dp[i-2][j-1] +deep(lca(i,j))}
*/
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int maxn=300005;
int n,K,a[maxn],f[maxn],dep[maxn],dp[3][maxn],lca[maxn];
vector<pii> ask[maxn];
vector<int> G[maxn];
void init() {
for (int i=1;i<=n;++i) {
f[i]=i;
G[i].clear();
ask[i].clear();
}
for (int i=1;i<n;++i) {
ask[a[i]].push_back(pii(i+1,a[i+1]));
ask[a[i+1]].push_back(pii(i+1,a[i]));
}
memset(dep,0,(n+1)*sizeof dep[0]);
}
int F(int x) {
return x==f[x]?x:f[x]=F(f[x]);
}
inline void U(int a,int b) {
f[F(b)]=F(a);
}
void dfs(int u,int d) {
dep[u]=d;
for (int i=0;i<(int)ask[u].size();++i) {
int j=ask[u][i].second,p=ask[u][i].first;
if (dep[j])
lca[p]=dep[F(j)];
}
for (int i=0;i<(int)G[u].size();++i) {
int v=G[u][i];
if (dep[v])
continue;
dfs(v,d+1);
U(u,v);
}
}
int main()
{
while (scanf("%d%d",&n,&K)==2) {
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
init();
for (int i=0;i<n-1;++i) {
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,1);
memset(dp[0],0x3f,(n+1)*sizeof dp[0][0]);
dp[0][0]=0;
for (int i=1;i<=n;++i) {
dp[i%3][0]=0;
for (int j=1;j<=K;++j) {
dp[i%3][j]=min(dp[(i-1)%3][j],dp[(i-1)%3][j-1]+dep[a[i]]);
if (i>=2)
dp[i%3][j]=min(dp[i%3][j],dp[(i+1)%3][j-1]+lca[i]);
}
}
printf("%d\n",dp[n%3][K]);
}
return 0;
}
HDU 6065 RXD, tree and sequence 分析+01背包
最新推荐文章于 2019-08-21 10:17:53 发布