BZOJ1040:[ZJOI2008]骑士——题解
http://www.lydsy.com/JudgeOnline/problem.php?id=1040
题面大意:n个人有一个价值和一个最恨的人,现在组出一个队伍使得价值最大且没有仇恨关系。
————————————————————
如果我们把被仇恨的人向仇恨他的人连一条有向边的话,那么我们不难发现,我们得到的图将会是若干个基环外向树(就是一个环,环上每个点都挂着一棵树)。
如果没有环,只有树的话,那么显然就是“没有上司的舞会”的那道题。
那么基本思路就没有变,f[i][0]表示i不选的时候的最大价值,f[i][1]表示i选的时候的最大价值。
我们显然可以先枚举树在环上的根,然后将环拆开成树,对这棵树进行一遍树形dp。
但是显然拆开部分也需要考虑,我们对拆开的边的另一个端点也进行一次树形dp,这样我们就有了所有的点的真实dp,我们就可以愉快的更新了。
#include<cstdio> #include<iostream> #include<cmath> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int N=1e6+5; inline int read(){ int X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch=='-';ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } struct node{ int to; int nxt; }edge[N]; int head[N],fa[N],val[N],num[N],cnt=0; ll ans,f[N][2]; bool vis[N]; inline void add(int u,int v){ cnt++; fa[v]=u; edge[cnt].to=v; edge[cnt].nxt=head[u]; head[u]=cnt; return; } void dfs(int u){ vis[u]=1; f[u][1]=val[u]; for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; if(!vis[v]){ dfs(v); f[u][0]+=max(f[v][0],f[v][1]); f[u][1]+=f[v][0]; } } return; } inline void dp(int u){ int rt; for(rt=u;num[rt]!=u;rt=fa[rt])num[rt]=u; dfs(rt); u=fa[rt]; f[u][1]=f[u][0]; for(u=fa[u];u!=rt;u=fa[u]){ f[u][0]=0;f[u][1]=val[u]; for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; f[u][0]+=max(f[v][0],f[v][1]); f[u][1]+=f[v][0]; } } f[rt][1]=val[rt]; for(int i=head[rt];i;i=edge[i].nxt){ int v=edge[i].to; f[rt][1]+=f[v][0]; } ans+=max(f[rt][0],f[rt][1]); return; } int main(){ int n=read(); for(int i=1;i<=n;i++){ val[i]=read(); add(read(),i); } for(int i=1;i<=n;i++){ if(!vis[i])dp(i); } printf("%lld\n",ans); return 0; }