这题开始我 WA40 了一个晚上。。。后来才发现是重建图的时候没有判断自环的情况
因为可能是有环的,所以用 Tarjan 将所有的环都缩起来,然后跑树形DP。
不知道 Luogu 上是怎么给到紫色难度的。。。
#include <iostream>
#include <cstdio>
#include <vector>
#include <stack>
const int MaxN = 500 + 5;
const int MaxM = 500 + 5;
int N, M, Scc, Idx;
int W[MaxN], V[MaxN], D[MaxN], WS[MaxN], VS[MaxN], Dfn[MaxN], Low[MaxN], Belong[MaxN], Dp[MaxN][MaxM], Indegree[MaxN];
bool InStack[MaxN], Book[MaxN][MaxN];
std::vector <int> Linker[MaxN];
std::stack <int> S;
inline int read()
{
register int x = 0;
register char ch = getchar();
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch))
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x;
}
void Tarjan(int cur)
{
Dfn[cur] = Low[cur] = ++Idx;
S.push(cur);
InStack[cur] = 1;
for(auto to : Linker[cur])
{
if(!Dfn[to])
{
Tarjan(to);
Low[cur] = std::min(Low[cur], Low[to]);
}
else if(InStack[to]) Low[cur] = std::min(Low[cur], Dfn[to]);
}
if(Dfn[cur] == Low[cur])
{
++Scc;
int t;
do
{
t = S.top();
S.pop();
InStack[t] = 0;
Belong[t] = Scc;
WS[Scc] += W[t];
VS[Scc] += V[t];
}
while(t != cur);
}
}
void DP(int cur)
{
for(auto to : Linker[cur])
{
DP(to);
for(int i = M - WS[cur]; i >= 1; --i)
for(int j = 0; j <= i; ++j) Dp[cur][i] = std::max(Dp[cur][i], Dp[cur][i - j] + Dp[to][j]);
}
for(int i = M; i >= WS[cur]; --i) Dp[cur][i] = Dp[cur][i - WS[cur]] + VS[cur];
for(int i = WS[cur] - 1; i >= 0; --i) Dp[cur][i] = 0;
}
int main()
{
N = read();
M = read();
for(int i = 1; i <= N; ++i) W[i] = read();
for(int i = 1; i <= N; ++i) V[i] = read();
for(int i = 1; i <= N; ++i)
{
D[i] = read();
if(!D[i]) continue;
Linker[D[i]].push_back(i);
}
for(int i = 1; i <= N; ++i) if(!Dfn[i]) Tarjan(i);
for(int i = 0; i <= N; ++i) Linker[i].clear();
int t = 0;
for(int i = 1; i <= N; ++i)
{
if(!Book[Belong[i]][Belong[D[i]]] && Belong[i] != Belong[D[i]])
{
Book[Belong[i]][Belong[D[i]]] = 1;
Linker[Belong[D[i]]].push_back(Belong[i]);
++Indegree[Belong[i]];
++t;
}
}
for(int i = 1; i <= Scc; ++i) if(!Indegree[i]) Linker[0].push_back(i);
DP(0);
printf("%d\n", Dp[0][M]);
return 0;
}