E - 流流流动

直接连边树形dp计算。需要注意的是图并非联通,可以使用并查集,判断这个集合是否已经处理过。

#include<bits/stdc++.h>
using namespace std;
const int maxn=110;
vector<int> e[maxn];
void add(int u,int v){e[u].push_back(v),e[v].push_back(u);}
int pre[maxn];
int fin(int x){return pre[x]==x?x:pre[x]=fin(pre[x]);}
void unio(int a,int b){pre[fin(a)]=fin(b);}
int f[maxn],d[maxn];
long long int dp[maxn][2];
bool vis[maxn];
void dfs(int u,int pre){
    dp[u][1]=f[u];
    for(int i=0;i<e[u].size();i++){
        int v=e[u][i];
        if(v==pre)continue;
        dfs(v,u);
        dp[u][0]+=max(dp[v][0],dp[v][1]);
        dp[u][1]+=max(dp[v][0],dp[v][1]-d[min(u,v)]);
    }
}

int main(){

    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){scanf("%d",&f[i]);pre[i]=i;}
    for(int i=1;i<=n;i++){scanf("%d",&d[i]);}
    for(int i=2;i<=n;i++){
        if((i&1) && (3*i+1<=n)){
            add(i,3*i+1);
            unio(i,3*i+1);
        }
        else if (i%2==0){
            add(i,i/2);
            unio(i,i/2);
        }
    }
    long long ans=0;
    for(int i=1;i<=n;i++){
             int p=fin(i);
        if(!vis[fin(i)]){
            vis[fin(i)]=1;
            dfs(fin(i),fin(i));
            ans+=max(dp[fin(i)][0],dp[fin(i)][1]);
        }
    }
    printf("%lld\n",ans);
    return 0;
}