题目链接
大概题意:
给你一棵树,求两颗不相交的子树使它们的点权总和最大,输出最大的点权和。点权可能为负。
输入:输入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;
}