CF325C Monsters and Diamonds 题解
一道二合一题。
首先 -1
的情况很好判断,可以和最小值一起搞。记 \(f_i\) 表示点 \(i\) 的答案,暴力更新的方法就是枚举每个该点的变换计算 \(\sum f_{p_i}[p_i\neq -1]+[p_i=-1]\) 的值最后再取个最小值。
做一遍 \(\tt dijistra\),一开始先将点 -1
入堆。每次取出一个点 \(i\) 时,将每个包含该点的变换的代价加上 \(f_i\),然后如果这个变换已经被每个 \(p_i\) 更新过了就更新一下这个变换对应的点的 \(f\) 值。
如果一个点的 \(f\) 值在这个过程中没被更新,那这个点就是 -1
的情况。
接下来求最大值,直接 \(\tt dfs\),如果一个点在这过程中可以访问到其 \(\tt dfs\) 树上的父亲或者一个答案为 -2
的点,那这个点的答案就是 -2
。剩下的情况直接暴力 \(\tt dp\) 就好了。
参考代码:
#include<bits/stdc++.h>
#define ll long long
#define mxn 100003
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
#define pq priority_queue
using namespace std;
struct node{
ll c;
vector<int>d;
};
int n,m,ed[mxn];
ll ds[mxn],dp[mxn],c[mxn],d1[mxn];
vector<node>e[mxn];
vector<int>f[mxn];
pq<pair<ll,int>>q;
bool v[mxn],ins[mxn];
void dfs(int x){
v[x]=1,ins[x]=1;
ll sm;
for(node s:e[x]){
for(int i:s.d)if(ds[i]>1e18)goto next;
sm=0;
for(int i:s.d){
if(!v[i]){
dfs(i);
if(dp[i]==-2)dp[x]=-2;
}else if(ins[i]||dp[i]==-2)dp[x]=-2;
sm+=dp[i];
}
if(dp[x]==-2)break;
dp[x]=min(max(dp[x],sm+s.c),314000000ll);
next:;
}
ins[x]=0;
}
signed main(){
scanf("%d%d",&m,&n);
for(int i=1,x,k,a;i<=m;++i){
scanf("%d%d",&x,&k);
c[i]=k,ed[i]=x;
node s;
rept(j,0,k){
scanf("%d",&a);
if(a!=-1)s.d.pb(a),f[a].pb(i);
else d1[i]++,f[0].pb(i);
}
s.c=d1[i];
e[x].pb(s);
}
memset(ds,0x3f,sizeof(ds));
q.push({0,0}),ds[0]=0;
while(q.size()){
int x=q.top().second;q.pop();
if(v[x])continue;
v[x]=1;
for(int i:f[x]){
d1[i]+=ds[x];
if(!--c[i]){
if(d1[i]<ds[ed[i]]){
ds[ed[i]]=d1[i];
q.push({-d1[i],ed[i]});
}
}
}
}
memset(v,0,sizeof(v));
rep(i,1,n)if(ds[i]<1e18&&!v[i])dfs(i);
rep(i,1,n){
if(ds[i]>1e18)puts("-1 -1");
else printf("%lld %lld\n",min(ds[i],314000000ll),dp[i]);
}
return 0;
}