这个题其实一个月前就想写了、但当时看到环基树就吓跑了、、
但其实这个题除了环要找出来单独跑一边dp、、别的就真的没了
先把环找出来(由于只有一个环,所以直接dfs)
然后对环上的每个点做一遍子树的dp
然后把环拆下来,枚举第一个点的状态、从这个点左边一直做到这个点右边
然后就没了、、注意它可能有多个子集、、
一个dp被我活生生写成了码农题、别的题解都40行、但还是调过了、码力看来还是可以的、
码:
#include<iostream>
#include<cstdio>
using namespace std;
#include<vector>
#define N 1000005
#define ll long long
vector<int>v[N],vv;
ll fu[N],h[N],f[N][3],g[N][3],n,a[N],x,ans,j,yici,d[N];
bool vis[N];
void dfs(int o,int fa,int dis)
{
d[o]=dis;
fu[o]=fa;
vis[o]=1;
int i;
for(i=0;i<v[o].size();i++)
{
int nd=v[o][i];
if(nd==fa)continue;
if(vis[nd])
{
if(yici==0&&d[nd]>d[o])
{
yici=1;
int x=nd;
while(x!=o)
{
vv.push_back(x);
h[x]=1;
x=fu[x];
}
h[x]=1;
vv.push_back(x);
}
}else dfs(nd,o,dis+1);
}
}
void dp(int o,int fa)
{
int i;
f[o][1]=a[o];
for(i=0;i<v[o].size();i++)
{
int nd=v[o][i];
if(h[nd]||nd==fa)continue;
dp(nd,o);
f[o][0]+=max(f[nd][0],f[nd][1]);
f[o][1]+=f[nd][0];
}
}
int main()
{
int i;
scanf("%lld",&n);
for(i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
scanf("%lld",&x);
v[x].push_back(i);
v[i].push_back(x);
}
for(i=1;i<=n;i++)
{
if(vis[i])continue;
vv.clear();
yici=0;
dfs(i,0,1);
if(vv.size()==0)
{
dp(i,0);
ans+=max(f[i][1],f[i][0]);
}
else
{
for(int k=0;k<vv.size();k++)
dp(vv[k],0);
//第一个不取
ll lin=0;
lin+=f[vv[0]][0];
g[vv[vv.size()-1]][0]=f[vv[vv.size()-1]][0];
g[vv[vv.size()-1]][1]=f[vv[vv.size()-1]][1];
for(j=vv.size()-2;j>0;j--)
{int o=vv[j],x=vv[j+1];
g[o][0]=f[o][0];
g[o][1]=f[o][1];
g[o][0]+=max(g[x][0],g[x][1]);
g[o][1]+=g[x][0];
}
lin+=max(g[vv[1]][1],g[vv[1]][0]);
//第一个取
ll lin2=0;
lin2+=f[vv[0]][1];
g[vv[vv.size()-1]][0]=f[vv[vv.size()-1]][0];
g[vv[vv.size()-1]][1]=0;
for(j=vv.size()-2;j>0;j--)
{int o=vv[j],x=vv[j+1];
g[o][0]=f[o][0];
g[o][1]=f[o][1];
g[o][0]+=max(g[x][0],g[x][1]);
g[o][1]+=g[x][0];
}
lin2+=g[vv[1]][0];
ans+=max(lin,lin2);
}
}
printf("%lld",ans);
}