BZOJ1040 [ZJOI2008]骑士 环套树/dp
借题区别了一下环套树和仙人掌
常见做法是拆环做2次DP
邻接表的话也能做,vis一下封堵点,第二次再vis到放行就好
//#include<bits/stdc++.h> #pragma comment(linker, "/STACK:1024000000,1024000000") #include<stdio.h> #include<algorithm> #include<queue> #include<string.h> #include<iostream> #include<math.h> #include<set> #include<map> #include<vector> #include<iomanip> using namespace std; #define ll long long #define pb push_back #define FOR(a) for(int i=1;i<=a;i++) const int inf=0x3f3f3f3f; const int maxn=1e6+5; ll a[maxn]; int n; struct EDGE{ int u,v,nxt; }G[maxn<<1]; int tot,head[maxn]; void init(){tot=0;memset(head,-1,sizeof head);} void addedge(int u,int v){ G[tot]=(EDGE){u,v,head[u]};head[u]=tot;tot++; G[tot]=(EDGE){v,u,head[v]};head[v]=tot;tot++; } bool s[maxn<<1]; ll f[maxn][2]; bool flag; int vis[maxn]; int l,r; void dfs(int u,int fa){ vis[u]=1; for(int i=head[u];~i && !flag;i=G[i].nxt){ if(G[i].v!=fa){ if(vis[G[i].v]){ l=u,r=G[i].v; s[i]=s[i^1]=1; flag=1; break; } dfs(G[i].v,u); } } } void dp(int u,int fa,int fob){ vis[u]=1; if(u!=fob)f[u][1]=a[u]; else f[u][1]=0; f[u][0]=0; for(int i=head[u];~i;i=G[i].nxt){ if(G[i].v!=fa && !s[i]){ dp(G[i].v,u,fob); f[u][0]+=max(f[G[i].v][0],f[G[i].v][1]); f[u][1]+=f[G[i].v][0]; } } } int main(){ scanf("%d",&n); init(); for(int i=1,x;i<=n;i++){ scanf("%lld%d",&a[i],&x); addedge(i,x); } ll ans=0; for(int i=1;i<=n;i++){ if(!vis[i]){ flag=false; dfs(i,0); ll tmp=0; dp(l,0,r); tmp=max(f[l][0],f[l][1]); dp(r,0,l); tmp=max(tmp,max(f[r][0],f[r][1])); ans+=tmp; } } printf("%lld\n",ans); }