P12734 理解

题目描述

沙季正在用悠太推荐的方法做现代文阅读练习。

\(n\) 个历史事件,编号为 \(1\)\(n\),其中每个历史事件可能有一个编号比它更小的前置事件,也可能没有。形式化地,对于事件 \(i\),用 \(p_i\) 表示其前置事件的编号,满足 \(p_i<i\),若 \(p_i=0\) 则表示它没有前置事件。

沙季有两种方式记起一个历史事件:回想和联想。如果她进行回想,那么她可以花费 \(r_u\) 时间,直接记起任意一个历史事件 \(u\);如果她进行联想,那么她可以选择任意一个已经记起来的事件 \(u\),并花费 \(t_v\) 时间记起一个满足 \(p_v=u\) 的事件 \(v\)

但是她的脑容量有限,因此她最多只能同时记起 \(k\) 个事件。她已经记起来的事件可以选择在任意时刻忘记,忘记事件不需要花费时间。为了防止记忆混乱,她不会再次记起任何曾经忘记过的事件。

现在,她有 \(m\) 道阅读题,解决其中的第 \(i\) 道题需要她记起事件 \(x_i\),她可以在记起事件 \(x_i\) 的时候立刻解决第 \(i\) 道题目,花费的时间忽略不计。她想要知道她至少需要花费多少时间才能解决所有题目。

数据范围

Subtask \(n,m\le\) 特殊性质 分值 依赖子任务
\(1\) \(10\) \(18\)
\(2\) \(10^5\) A \(18\)
\(3\) \(10^5\) B \(18\)
\(4\) \(10^5\) C \(18\)
\(5\) \(10^5\) \(28\) \(1,2,3,4\)

特殊性质 A:保证 \(p_i=0\)\(p_i=i-1\)

特殊性质 B:保证 \(p_i=\lfloor\frac i2\rfloor\)

特殊性质 C:保证 \(p_i\le1\)

对于所有数据,满足 \(1\le T\le5\)\(1\le n,m\le10^5\)\(1\le k\le10\)\(0\le p_i<i\)\(0\le r_i,t_i\le10^9\)\(1\le x_i\le n\)

Analysis

显然这是一道树形 DP。

\(F_u\) 为不选择历史事件 \(u\) 时将子树 \(u\) 内所有需要回忆的历史事件全部回忆所需的时间,\(f_{u,k}\) 表示历史事件 \(u\) 已经被回忆且目前还可以再回忆 \(k\) 个历史事件的情况下,将子树 \(u\) 内所有需要回忆的历史事件全部回忆所需的时间。

先考虑 \(F_u\) 的状态转移方程:若历史事件 \(u\) 需要被回忆,则 \(F_u=\infty\)。否则有状态转移方程:

\[F_u=\sum_{v\in son(u)}\min(F_v,f_{v,K-1}+r_v) \]

再考虑 \(f_{u,k}\) 的状态转移方程。

对于 \(u\) 的儿子 \(v\),有三种情况:

  1. 单独回忆子树 \(v\) 内的所有需要回忆的历史事件,此时的贡献为 \(\min(F_v,f_{v,K-1}+r_v)\)
  2. 回忆历史事件 \(v\),不忘记历史事件 \(u\),然后将子树 \(v\) 内所有需要回忆的历史事件全部回忆。此时的贡献为 \(f_{v,k-1}+t_v\)
  3. 回忆历史事件 \(v\)忘记历史事件 \(u\),然后将子树 \(v\) 内所有需要回忆的历史事件全部回忆。由于题目说明不会再次记起任何曾经忘记过的事件,所以这种情况只会出现一次。且要在其他儿子的子树内的所有需要回忆的历史事件均被回忆后才能回忆历史事件 \(v\)

\(f_{u,k}\) 的状态转移方程为:

\[f_{i,k}=\min(\sum_{v\in son(u)}\min\{F_v,f_{v,K-1}+r_v,f_{v,k-1}+t_v\},\min_{v\in son(u)}\{f_{v,k}+t_v+\sum_{w\in son(u),w\not=v}\min\{F_w,f_{w,K-1}+r_w,f_{w,k-1}+t_w\}\}) \]

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Inf=1.9e17;
const int N=1e5+999;

int h[N],nt[N],to[N],cnt;
inline void lnk(int x,int y){
	nt[++cnt]=h[x],h[x]=cnt,to[cnt]=y;
}
int n,m,K,p[N],R[N],T[N];
LL f[N][12],F[N];
bool bo[N],O[N]; // bo:若子树 u 中存在需要回忆的事件,则为 true,用于剪枝,其实没啥用。
inline void dp(int u){
	if(!bo[u])return;
	for(int i=h[u];i;i=nt[i])dp(to[i]);
	memset(f[u],0,sizeof f[u]);F[u]=0;
	for(int i=h[u],v=to[h[u]];i;i=nt[i],v=to[i]){
		if(!bo[v])continue;
		F[u]+=min(F[v],f[v][K-1]+R[v]);
		for(int k=0;k<=K;k++)
			f[u][k]+=min((k?f[v][k-1]+T[v]:Inf),min(F[v],f[v][K-1]+R[v]));
	}
	for(int k=1;k<K;k++){
		LL tmp=f[u][k];
		for(int i=h[u],v=to[h[u]];i;i=nt[i],v=to[i]){
			if(!bo[v])continue;
			f[u][k]=min(f[u][k],tmp-min((k?f[v][k-1]+T[v]:Inf),min(F[v],f[v][K-1]+R[v]))+f[v][k]+T[v]);
		}
	}
	if(O[u])F[u]=Inf;
	for(int i=1;i<=K;i++)
		f[u][i]=min(f[u][i],f[u][i-1]);
}

int main(){
#ifdef LOCAL
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	int tt;scanf("%d",&tt);
	while(tt--){
		scanf("%d%d%d",&n,&m,&K);
		cnt=0,memset(h,0,(n+3)<<2);
		memset(bo,0,n+3);memset(O,0,n+3);
		for(int i=1;i<=n;i++)scanf("%d",&p[i]),lnk(p[i],i);
		for(int i=1;i<=n;i++)scanf("%d",&R[i]);
		for(int i=1;i<=n;i++)scanf("%d",&T[i]);
		for(int i=1,x;i<=m;i++)scanf("%d",&x),O[x]=bo[x]=true;
		for(int i=n;i;i--)bo[p[i]]|=bo[i];
		dp(0);
		cout<<F[0]<<endl;
	}
	return 0;
}
posted @ 2025-06-13 19:53  fzrcy  阅读(8)  评论(0)    收藏  举报