P2607 [ZJOI2008] 骑士
题目
思路
还是这张图,这题读入方式和这题一样。所以代码结构上很像。
- 把环上每个点看做树根,往环外做树形DP。
- 任意找到环上一点,遍历一遍。处理一下首尾的关系。
- 用拓扑实现起来简单。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10;
int read(){
int x=0,f=1;char c=getchar();
while(c>'9' || c<'0'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
int dp[N][2],n,va[N],to[N],ind[N],f[N][2],f2[N][2],ans;
queue<int>q;
int work(int u){
int now=u;
f[u][1]=dp[u][1],f[u][0]=-1e15;//选第一个点
f2[u][1]=-1e15,f2[u][0]=dp[u][0];//不选第一个点
while(to[now]!=u){ind[now]=0;
f[to[now]][0]+=max(f[now][0],f[now][1])+dp[to[now]][0];
f[to[now]][1]+=f[now][0]+dp[to[now]][1];
f2[to[now]][0]+=max(f2[now][0],f2[now][1])+dp[to[now]][0];
f2[to[now]][1]+=f2[now][0]+dp[to[now]][1];
now=to[now];
}
ind[now]=0;
return max(f[now][0],max(f2[now][0],f2[now][1]));
}
signed main(){
n=read();
for(int i=1;i<=n;i++)va[i]=read(),to[i]=read(),ind[to[i]]++;
for(int i=1;i<=n;i++){dp[i][1]=va[i];if(!ind[i])q.push(i);}
while(!q.empty()){
int u=q.front();q.pop();
dp[to[u]][0]+=max(dp[u][1],dp[u][0]);
dp[to[u]][1]+=dp[u][0];
ind[to[u]]--;if(!ind[to[u]])q.push(to[u]);
}
for(int i=1;i<=n;i++)if(ind[i])ans+=work(i);
printf("%lld\n",ans);
return 0;
}