[BZOJ2427][HAOI2010]软件安装(tarjan+树形DP)

如果依赖关系出现环,那么对于一个环里的点,要么都选要么都不选,

所以每个环可以当成一个点,也就是强连通分量

然后就可以构造出一颗树,然后树形背包瞎搞一下就行了

注意要搞一个虚拟节点当根节点

Code

#include <cstdio>
#include <algorithm>
#include <cstring>
#define N 2010
using namespace std;

struct info{int to,nex;}e[N],ne[N];
int n,m,w[N],v[N],tot,head[N],d[N],in[N];
int dfn[N],low[N],scc,tp,sta[N],bl[N],wei[N],val[N],dp[N][N];
bool inq[N];

void Link(int u,int v){
	e[++tot].nex=head[u];head[u]=tot;e[tot].to=v;
}

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

void tarjan(int u){
	dfn[u]=low[u]=++tot;
	sta[++tp]=u,inq[u]=1;
	for(int i=head[u],v;i;i=e[i].nex)
		if(!dfn[v=e[i].to]) tarjan(v),low[u]=min(low[u],low[v]);
		else if(inq[v]) low[u]=min(low[u],dfn[v]);
	if(dfn[u]==low[u]){
		++scc;
		for(int v=0;v!=u;)inq[v=sta[tp--]]=0,bl[v]=scc;
	}
}

void DP(int u){
	for(int i=wei[u];i<=m;++i) dp[u][i]=val[u];
	for(int i=head[u],v;i;i=e[i].nex){
		DP(v=e[i].to);
		for(int j=m-wei[u];j>=0;--j)
			for(int k=0;k<=j;++k)
				dp[u][j+wei[u]]=max(dp[u][j+wei[u]],dp[u][j+wei[u]-k]+dp[v][k]);
	}
}

int main(){
	n=read(),m=read();
	for(int i=1;i<=n;w[i++]=read());
	for(int i=1;i<=n;v[i++]=read());
	for(int i=1;i<=n;++i) if(d[i]=read()) Link(d[i],i);
	tot=0;
	for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
	tot=0,memset(head,0,sizeof(head)),memset(e,0,sizeof(e));
	for(int i=1;i<=n;++i){
		wei[bl[i]]+=w[i],val[bl[i]]+=v[i];
		if(bl[i]!=bl[d[i]]&&d[i]) Link(bl[d[i]],bl[i]),in[bl[i]]++;
	}
	++scc;
	for(int i=1;i<scc;++i) if(!in[i]) Link(scc,i);
	DP(scc);
	printf("%d\n",dp[scc][m]);
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/void-f/p/9122999.html