8.25总结

T3

首先是无根树,考虑枚举根统计答案

自底向上,如果一个点有比2个多的子树,选择子树大小最大的两个保留

我们统计保留的节点数量,答案就是n-sz[root]

#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int maxn=3e2+5,mod=1e9+7,inf=1e18;
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
void write(int x){if(x<0){putchar('-'),x=-x;}if(x>9){write(x/10);}putchar(x%10+'0');return;}
int fpow(int a,int b,int p){if(b==0){return 1;}int res=fpow(a,b/2,p)%p;if(b%2==1){return((res*res)%p*a)%p;}else{return(res*res)%p;}}
int t,n;
vector<int>vt[maxn];
int sz[maxn],cnt;
void dfs(int x,int ff){
	int cnt=0;
	sz[x]=1;
	for(int v:vt[x]){
		if(v==ff)continue;
		dfs(v,x);
		cnt++;
	}
	if(cnt<=1){
		return;
	}
	int m1=0,m2=0;
	for(int v:vt[x]){
		if(v==ff){
			continue;
		}
		if(m1<sz[v]){
			m2=m1;
			m1=sz[v];
		}
		else if(m2<sz[v]){
			m2=sz[v];
		}
		sz[x]=m1+m2+1;
	}
}
void solve(){
	cin>>n;
	cnt++;
	for(int i=1;i<=n;i++){
		vt[i].clear();
	}
	for(int i=1;i<n;i++){
		int x,y;
		cin>>x>>y;
		vt[x].push_back(y);
		vt[y].push_back(x);
	} 
	int ans=n;
	for(int i=1;i<=n;i++){
		dfs(i,0);
		ans=min(ans,n-sz[i]);
	}
	cout<<"Case #"<<cnt<<": "<<ans<<endl;
}
signed main(){
	srand(time(0));
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

T4

  1. 问题分析:问题要求计算在树结构中击败所有怪物的最小能量消耗,考虑使用魔法的次数。每个节点的能量消耗取决于其自身血量和其存活子节点的血量之和。
  2. 关键观察:正常击败节点i的能量消耗为hp_i加上其所有未被魔法击败的子节点的血量之和。魔法击败节点消耗0能量且无顺序限制。
  3. 动态规划:使用树形动态规划来记录状态:
    • dp[0][u][j]表示节点u未被魔法击败,且在其子树中使用了j次魔法的最小能量消耗。
    • dp[1][u][j]表示节点u被魔法击败,且在其子树中使用了j次魔法的最小能量消耗。
  4. 状态转移:对于每个节点u,遍历其子节点v,合并状态:
    • 如果u和v均未被魔法击败,需加上v的血量作为额外消耗。
    • 其他情况直接合并状态而不加额外消耗。
  5. 结果提取:根节点的状态dp[0][1][j]dp[1][1][j]中的最小值即为使用j次魔法时的最小能量消耗。

解决代码

#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int maxn=4e3+5,mod=1e9+7,inf=1e18;
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
void write(int x){if(x<0){putchar('-'),x=-x;}if(x>9){write(x/10);}putchar(x%10+'0');return;}
int fpow(int a,int b,int p){if(b==0){return 1;}int res=fpow(a,b/2,p)%p;if(b%2==1){return((res*res)%p*a)%p;}else{return(res*res)%p;}}
int t,n,m;
int dp[2][maxn][maxn],h[maxn],cnt,sz[maxn];
vector<int>vt[maxn];
void init(int n){
	cnt=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=n;j++){
			dp[0][i][j]=dp[1][i][j]=inf;
		}
	}
}
void dfs(int x,int ff){
	dp[0][x][0]=0;
	dp[1][x][1]=h[x];
	sz[x]=1;
	for(int v:vt[x]){
		if(v==ff)continue;
		dfs(v,x);
		
		for(int j=sz[x]+sz[v];j>=0;j--){
			int tmp=min(j,sz[v]);
			for(int k=max(0ll,j-sz[x]);k<=tmp;k++){
				dp[1][x][j]=min(dp[1][x][j],dp[1][x][j-k]+min(dp[0][v][k],dp[1][v][k]+h[v]));
				dp[0][x][j]=min(dp[0][x][j],dp[0][x][j-k]+min(dp[0][v][k],dp[1][v][k]));
			}
		}sz[x]+=sz[v];
	}
}
void solve(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		vt[i].clear();
	}
	init(n);
	for(int i=2;i<=n;i++){
		int x;
		cin>>x;
		vt[i].push_back(x);
		vt[x].push_back(i);
	}
	for(int i=1;i<=n;i++){
		cin>>h[i];
	}
	dfs(1,0);
	for(int i=n;i>=n-m;i--){
		cout<<min(dp[0][1][i],dp[1][1][i])<<' ';
	}
	cout<<endl;
}
signed main(){
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}
posted @ 2025-08-25 10:45  KK_SpongeBob  阅读(6)  评论(0)    收藏  举报