~~~~~ P2515 [HAOI2010] 软件安装 ~~~~~ 总题单链接
思路
~~~~~ 发现构成的图是一个森林和一些环。
~~~~~ 对于森林,建一个虚点然后树形 D P DP DP 即可。
~~~~~ 对于环,发现要么把这个环上的每一个点都选了,要么每一个都不选。所以可以先缩点。
~~~~~ 缩点后跑树形 d p dp dp 就行了。
代码
#include<bits/stdc++.h>
#define ll long long
#define fir first
#define sec second
using namespace std;
ll n,m,v[505],w[505];
ll dfn[505],low[505],tot;
ll scc[505],din[505],cnt;
ll stk[505],ins[505],top;
vector<ll>eg[505],ng[505];
ll scw[505],scv[505],dp[505][505];
void Tarjan(ll p){
dfn[p]=low[p]=++tot;
stk[++top]=p;ins[p]=1;
for(ll v:eg[p]){
if(!dfn[v]){
Tarjan(v);
low[p]=min(low[p],low[v]);
}
else if(ins[v])low[p]=min(low[p],dfn[v]);
}
if(low[p]==dfn[p]){
cnt++;
while(1){
ll z=stk[top--];
ins[z]=0;
scc[z]=cnt;
scv[cnt]+=v[z];
scw[cnt]+=w[z];
if(z==p)break;
}
}
}
void dfs_dp(ll p){
if(scv[p]<=m)dp[p][scv[p]]=scw[p];
for(ll v:ng[p]){
dfs_dp(v);
for(ll i=m;i>=scv[p];i--)
for(ll j=m;j>=0;j--)
if(i+j<=m)
dp[p][i+j]=max(dp[p][i+j],dp[p][i]+dp[v][j]);
}
}
signed main(){
ios::sync_with_stdio(false);
cin>>n>>m;
for(ll i=1;i<=n;i++)cin>>v[i];
for(ll i=1;i<=n;i++)cin>>w[i];
for(ll i=1;i<=n;i++){
ll f;cin>>f;
eg[f].push_back(i);
}
for(ll i=1;i<=n;i++)
if(!dfn[i])Tarjan(i);
for(ll u=1;u<=n;u++)
for(ll v:eg[u]){
if(scc[u]==scc[v])continue;
ng[scc[u]].push_back(scc[v]);
din[scc[v]]++;
}
for(ll i=1;i<=cnt;i++)
if(!din[i])ng[0].push_back(i);
memset(dp,-0x3f,sizeof(dp));
dfs_dp(0);
ll ans=0;
for(ll i=0;i<=m;i++)
ans=max(ans,dp[scc[0]][i]);
cout<<ans;
return 0;
}