题目链接
大概题意:
给你一棵树,求两颗不相交的子树使它们的点权总和最大,输出最大的点权和。点权可能为负。
输入:输入N,N表示树节点的位置,接下来一个输入N个数分别表示节点的权值,接下来N-1行输入两个数表示这两个点存在一条无向边。
输出:输出两颗不相交的子树使它们的点权总和最大。
思路:
总体思路用树状数组。第一遍dfs求出以每个节点为根节点的树的点权和,用f数组存下来。第二遍dfs用DP数组存下以各个节点为根节点的树的所有子树的点权和的最大值。
#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
const ll minx=-1e18;
//const ll maxx=1e18;
int n;
int a[200005];
vector<int > g[200005];
ll f[200005];//f数组记录着以x为根节点的树上各点的点权和
ll dp[200005];//dp数组表示以x为根节点的树中,所有子树存在的最大的点权和
ll ans=minx;
ll max(ll a,ll b){
if(a>=b) return a;
return b;
}
ll dfs1(int fa,int x){//求以各个节点的根的树的点权和
int len=g[x].size();
if(len==1&&x!=1){
f[x]=a[x];
return f[x];
}
ll sum=a[x];
for(int i=0;i<len;i++){
if(g[x][i]==fa) continue;
ll temp=dfs1(x,g[x][i]);
sum+=temp;
}
f[x]=sum;
return f[x];
}
ll dfs2(int fa,int x){
int len=g[x].size();
if(len==1&&x!=1){
dp[x]=f[x];
return dp[x];
}
dp[x]=f[x];//这步很重要
ll max1=minx,max2=minx,temp;//max1表示最大值,max2表示次大值
for(int i=0;i<len;i++){
if(g[x][i]==fa) continue;
temp=dfs2(x,g[x][i]);
if(temp>max1){//如果子树的最大点权和比最大值大,更新最大值
max2=max1;//**同时更新最小值**
max1=temp;
}
else{//如果比最大值小,那么就与次小值比较,更新次小值
max2=max(max2,temp);
}
//dp[x]取以x为根节点的树中,所有子树存在的最大的点权和
dp[x]=max(dp[x],temp);
}
//如果max1和max2都被更新了,那么说明以x为根节点会存在两个不相连的子树,那么更新ans值
if(max1!=minx&&max2!=minx){
ans=max(ans,max1+max2);
}
return dp[x];
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<n;i++){
int x,y;
cin>>x>>y;
g[x].push_back(y);
g[y].push_back(x);
}
dfs1(0,1);
dfs2(0,1);
if(ans==minx) cout<<"Impossible"<<endl;
else cout<<ans<<endl;
return 0;
}
京公网安备 11010502036488号