用f[x]表示彻底把x消灭的代价
用g[x]表示把x的衍生物(不包括x)彻底杀死的代价
那么一开始f[x]就等于膜法攻击的代价,g[x]为膜法把x的衍生物逐个杀死的代价总和
之后不断用一个队列(类似SPFA的思想)不断更新维护f[x]就可以了
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<iostream>
using namespace std;
long long f[200005];//彻底
long long g[200005];//衍生
long long pa[200005],mo[200005];
int first1[200005],first2[200005];
int len1=0,len2=0;
struct mod{int x,y,next;};
mod q1[1000005],q2[1000005];
queue<int>t;
bool v[200005];
int st=1,ed=1,n;
void ins1(int x,int y)
{
len1++;
q1[len1].x=x;
q1[len1].y=y;
q1[len1].next=first1[x];
first1[x]=len1;
}
void ins2(int x,int y)
{
len2++;
q2[len2].x=x;
q2[len2].y=y;
q2[len2].next=first2[x];
first2[x]=len2;
}
void SPFA()
{
while(!t.empty())
{
int x=t.front();
t.pop();
if (pa[x]+g[x]<f[x])
{
for (int j=first2[x];j!=0;j=q2[j].next)
{
int fa=q2[j].y;
if (v[fa]==false)
{
t.push(fa);
}
g[fa]=g[fa]-f[x]+pa[x]+g[x];
}
f[x]=pa[x]+g[x];
}
v[x]=false;
}
}
int main()
{
memset(first1,0,sizeof(first1));
memset(first2,0,sizeof(first2));
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
int R;
scanf("%lld%lld%d",&pa[i],&mo[i],&R);
for (int j=1;j<=R;j++)
{
int y;
scanf("%d",&y);
ins1(i,y);
ins2(y,i);
}
}
for (int i=1;i<=n;i++)
{
f[i]=mo[i];
t.push(i);
v[i]=true;
g[i]=0;
}
for (int x=1;x<=n;x++)
{
for (int j=first1[x];j!=0;j=q1[j].next)
{
int y=q1[j].y;
g[x]+=f[y];
}
}
SPFA();
printf("%lld\n",f[1]);
}