P2515 [HAOI2010] 软件安装

P2515 [HAOI2010] 软件安装

Description

我们考虑安装一些软件,软件之间有一些依赖关系,每个软件有价值 \(v_i\) 和代价 \(w_i\)

求在空间为 \(m\) 的情况下,价值的最大值。

Solution

首先考虑缩点,因为在同一个强联通分量内的点要选则必须一起选,我们考虑维护一个 \(W_i\)\(V_i\) 表示缩点后的整体代价和价值。

对于入度为 0 的点,我们考虑从 0 连一条边指向它,最后考虑树上背包即可。

\(f_{i,j}\) 表示对于以 \(i\) 为根的子树,考虑 \(j\) 个空间的最大价值。

转移:\(dp_{x,i+W_x}=\max(dp_{son_x,j+dp_x,i+W_x-j})\)。即在选它的情况下,给它分配 \(i\) 个空间,并且给它的某个儿子分配 \(j\) 个空间。

Code

#include<bits/stdc++.h>
using namespace std;

const int N=1e3+7;

int a[N],w[N];
int n,m;
vector<int> G[N];
int d[N];
int dfn[N],low[N],dfn_clock;
stack<int> s;
int tot,belongs[N];
bool vis[N];
vector<int> G2[N];
int rd[N];
int dp[N][N];
int scc_w[N];
int scc_v[N];

void tarjan(int x){
  dfn[x]=low[x]=++dfn_clock;vis[x]=1;s.push(x);
  for(int k:G[x]){
    if(!dfn[k]) tarjan(k),low[x]=min(low[x],low[k]);
    else if(vis[k]) low[x]=min(low[x],dfn[k]);
  }
  if(dfn[x]==low[x]){
    ++tot;
    int k=0;
    while(x!=k){
      k=s.top(),vis[k]=0,s.pop(),belongs[k]=tot;
      scc_w[tot]+=w[k],scc_v[tot]+=a[k];
    }
  }
}

void solve(int x){
  for(int i=scc_w[x];i<=m;i++) dp[x][i]=scc_v[x];
  for(int k:G2[x]){
    solve(k);int left=m-scc_w[x];
    for(int i=left;i>=0;i--){
      for(int j=0;j<=i;j++){
        dp[x][i+scc_w[x]]=max(dp[x][i+scc_w[x]],dp[k][j]+dp[x][i+scc_w[x]-j]);
      }
    }
  }
}
int main(){
  scanf("%d%d",&n,&m);
  for(int i=1;i<=n;i++) scanf("%d",&w[i]);
  for(int i=1;i<=n;i++) scanf("%d",&a[i]);
  for(int i=1;i<=n;i++){
    scanf("%d",&d[i]);if(d[i]) G[d[i]].push_back(i);
  }
  for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);

  for(int i=1;i<=n;i++){
    if(belongs[d[i]]!=belongs[i]){
      G2[belongs[d[i]]].push_back(belongs[i]);
      rd[belongs[i]]++;
    }
  }
  for(int i=1;i<=tot;i++) if(!rd[i]) G2[0].push_back(i);
  solve(0);
  printf("%d",dp[0][m]);
  return 0;
}
posted @ 2023-09-04 08:56  Zimo_666  阅读(24)  评论(0编辑  收藏  举报