[HAOI2010]软件安装

link

如果没有环的话其实就是一个比较水的树形背包。但是现在有环,并且当我们选择环中任何一点时其他环上的点也都要被选择,然后就可以缩点解决环的问题。

然后缩点后会形成一个森林,我们建虚点$0$连向各自联通块的根,然后做一个树上背包即可

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=1001;
struct node{
    int u,v,nex;
}x[MAXN<<1];
struct node1{
    int u,v;
}E[MAXN];
int dfn[MAXN],uu[MAXN<<1],CNT,low[MAXN],cnt,n,w,num,col[MAXN],co,head[MAXN],v[MAXN],W[MAXN];
void add(int u,int v){
    x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++;
}
int sta[MAXN],tot;
int ww[MAXN],vv[MAXN];
void tarjan(int f,int fath){
    dfn[f]=low[f]=++num;sta[++tot]=f;
    for(int i=head[f];i!=-1;i=x[i].nex){
        int v=x[i].v;
        if(!dfn[v]) tarjan(v,f),low[f]=min(low[f],low[v]);
        else if(!col[v]) low[f]=min(low[f],dfn[v]);
    }
    if(low[f]==dfn[f]){
        col[f]=++co;
        while(sta[tot]!=f){
            col[sta[tot]]=co;
            ww[co]+=W[sta[tot]],vv[co]+=v[sta[tot]];
            tot--;
        }
        ww[co]+=W[sta[tot]],vv[co]+=v[sta[tot]];
        tot--;
    }return;
}
int f[MAXN];
bool cmp(node1 x1,node1 x2){
    if(x1.u==x2.u) return x1.v<x2.v;
    return x1.u<x2.u;
}
int find(int xx){
    if(f[xx]==xx) return xx;
    return f[xx]=find(f[xx]);
}
void merge(int x1,int x2){
    int t1=find(x1),t2=find(x2);
    f[t2]=t1;
    return;
}
int group[MAXN][MAXN],size[MAXN];
void dfs(int f){
    size[f]=1;
    for(int i=head[f];i!=-1;i=x[i].nex){
        dfs(x[i].v);
        size[f]+=size[x[i].v];
    }return;
}
int _MAXN[MAXN],root[MAXN];
void build(){
    for(int i=1;i<=co;i++) f[i]=i;
    for(int i=1;i<=CNT;i++)
        E[i].u=col[E[i].u],E[i].v=col[E[i].v];
    sort(E+1,E+CNT+1,cmp);
    memset(head,-1,sizeof(head)),cnt=0;
    for(int i=1;i<=CNT;i++){
        if((E[i].u!=E[i].v)&&(E[i].u!=E[i-1].u||E[i].v!=E[i-1].v)){
            merge(E[i].u,E[i].v);
            add(E[i].u,E[i].v);
        }
    }
    memset(root,-1,sizeof(root));
    for(int i=1;i<=co;i++) f[i]=find(f[i]);
    for(int i=1;i<=co;i++){
        memset(size,0,sizeof(size));
        dfs(i);
        if(size[i]>_MAXN[f[i]]){
            _MAXN[f[i]]=size[i];
            root[f[i]]=i;
        }
    }
    for(int i=1;i<=co;i++)
        if(root[i]!=-1)add(0,i);
    ww[0]=1,vv[0]=0;
    w++;
}
int dp[MAXN][MAXN];
void dp_tree(int f,int fath){
    for(int k=head[f];k!=-1;k=x[k].nex){
        int v=x[k].v;
        dp_tree(v,f);
        for(int i=w;i>=0;i--){
            for(int j=i;j>=0;j--) if(i-j>=ww[v]) {
                dp[f][i]=max(dp[f][i],dp[f][j]+dp[v][i-j]);
            }
        }
    }
    for(int i=w;i>=ww[f];i--) dp[f][i]=dp[f][i-ww[f]]+vv[f];
}
int main(){
    memset(head,-1,sizeof(head));
    n=read(),w=read();
    for(int i=1;i<=n;i++) W[i]=read();
    for(int i=1;i<=n;i++) v[i]=read();
    for(int i=1;i<=n;i++){
        int u=read();
        if(u) {
            E[++CNT].u=u,E[CNT].v=i;
            add(u,i); 
        }
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i]) tarjan(i,0);
    build();
    dp_tree(0,0);
    cout<<dp[0][w];
}
View Code
原文地址:https://www.cnblogs.com/si-rui-yang/p/10086524.html