[ZJOI2008] 骑士
BZOJ
luogu
我们发现关系图由恰好n个点,n条边构成,
这告诉我们关系图是一些基环树,
我们可以找出这个环,对环上每个点的子树dp一遍
再把这个环dp一遍求出答案
树dp:设(f_i)表示选择i的子树最大价值和
(g_i)表示不选i的子树最大价值和
[f_u+=g_v$$$$g_u+=max(f_v,g_v)
]
环dp:相同的状态和转移,讨论是否选1做两遍dp
博猪抠环的方法好像复杂了,不过过了就懒得管了...
注意图不一定连通和有重边
#define ll long long
#include<bits/stdc++.h>
using namespace std;
const int _=1e6+5;
int re(){
int x=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*w;
}
int n,cnt,tot,top,sz,ts;
int h[_],cir[_],st[_],fa[_],dfn[_],low[_],tmp[_];
ll Ans,ans,g[_],f[_],F[_],G[_];
bool vis[_];
struct edge{int to,next;}e[_<<1];
void link(int u,int v){
e[++cnt]=(edge){v,h[u]};h[u]=cnt;
e[++cnt]=(edge){u,h[v]};h[v]=cnt;
}
void tarjan(int u){
dfn[u]=low[u]=++ts;st[++top]=u;
for(int i=h[u];i;i=e[i].next){
int v=e[i].to;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
int x;sz=0;
do{x=st[top--];tmp[++sz]=x;}while(x^v);tmp[++sz]=u;
if(sz>tot){tot=sz;for(int j=1;j<=sz;j++)cir[j]=tmp[j];}
}
}
else low[u]=min(low[u],dfn[v]);
}
}
void dfs(int u){
for(int i=h[u];i;i=e[i].next){
int v=e[i].to;
if(vis[v]||v==fa[u]||fa[v]==u)continue;//重边
fa[v]=u;dfs(v);f[u]+=g[v];g[u]+=max(f[v],g[v]);
}
}
int main(){
n=re();
for(int i=1;i<=n;i++){
f[i]=re();link(i,re());
}
for(int i=1;i<=n;i++){
if(dfn[i])continue;
top=0;tot=0;tarjan(i);
for(int j=1;j<=tot;j++)vis[cir[j]]=1;
for(int j=1;j<=tot;j++){
dfs(cir[j]);F[j]=f[cir[j]];G[j]=g[cir[j]];
}
G[2]+=F[1];F[3]+=G[2];G[3]+=G[2];//选择1
for(int j=4;j<=tot;j++){
F[j]+=G[j-1];
G[j]+=max(F[j-1],G[j-1]);
}
ans=G[tot];
for(int j=1;j<=tot;j++){
F[j]=f[cir[j]];G[j]=g[cir[j]];
}
F[2]+=G[1];G[2]+=G[1];//不选择1
for(int j=3;j<=tot;j++){
F[j]+=G[j-1];
G[j]+=max(F[j-1],G[j-1]);
}
ans=max(ans,max(F[tot],G[tot]));
Ans+=ans;
}
printf("%lld
",Ans);
return 0;
}