rt,我的想是对于每颗基环树,求出环上每个节点不经过其他环上节点的 dp 值,然后再在环上用环形 dp,dp 两次的方法处理。
但是题解里都是断边然后对于边的两端分别树形 dp,而我的做法只能拿 40 pts,代码应该没错,想问问思路错在哪里。
我的代码,应该还挺好懂的
// Problem: P2607 [ZJOI2008] 骑士
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2607
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define ll long long
using namespace std;
vector<ll> l[1000001];
vector<ll> g[1000001];
ll val[1000001],fa[1000001];
ll vis[1000001];
ll lop[1000001],dp[1000001][2];
ll dp2[1000001][2],dfn[1000001];
ll tr[1000001];
ll tot,all,num;
void dfs(ll x,ll f){
fa[x]=f;dfn[x]=++all;vis[x]=vis[f];
for(ll i=0;i<l[x].size();i++){
ll v=l[x][i];
if(v==f) continue;
if(!dfn[v]) dfs(v,x);
else{
if(dfn[v]<dfn[x]) continue;
g[vis[x]].push_back(v);
for(ll j=fa[v];j!=x;j=fa[j])g[vis[x]].push_back(j);
g[vis[x]].push_back(x);
}
}
}
void dfs2(ll x,ll f){
dp[x][1]=val[x];
for(ll i=0;i<l[x].size();i++){
ll v=l[x][i];
if(v==f || lop[v]) continue;
dfs2(v,x);
dp[x][1]+=dp[v][0];
dp[x][0]+=max(dp[v][1],dp[v][0]);
}
}
ll calc(ll bel){
dp2[0][1]=dp[g[bel][0]][1];
ll ans=0;
for(ll i=1;i<g[bel].size();i++){
dp2[i][1]=dp[g[bel][i]][1]+dp2[i-1][0];
dp2[i][0]=max(dp2[i-1][1],dp2[i-1][0])+dp[g[bel][i]][0];
}
ans=max(ans,dp2[g[bel].size()-1][0]);
dp2[0][1]=0;
for(ll i=1;i<g[bel].size();i++){
dp2[i][1]=dp[g[bel][i]][1]+dp2[i-1][0];
dp2[i][0]=max(dp2[i-1][1],dp2[i-1][0])+dp[g[bel][i]][0];
}
ans=max(ans,max(dp2[g[bel].size()-1][0],dp2[g[bel].size()-1][1]));
return ans;
}
int main(){
ll n,v;
cin>>n;
for(ll i=1;i<=n;i++){
cin>>val[i]>>v;
l[i].push_back(v);
l[v].push_back(i);
}
for(ll i=1;i<=n;i++){
if(!dfn[i]){
vis[0]=++num;
tr[num]=i;
dfs(i,0);
}
}
for(ll i=1;i<=num;i++)for(ll j=0;j<g[i].size();j++)lop[g[i][j]]++;
ll ans=0;
for(ll i=1;i<=num;i++){
if(!g[i].size()){
dfs2(tr[i],0);
ans+=max(dp[tr[i]][0],dp[tr[i]][1]);
}
for(ll j=0;j<g[i].size();j++)dfs2(g[i][j],0);
}
for(ll i=1;i<=num;i++)ans+=calc(i);
cout<<ans;
}