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;
}
posted @ 2023-11-25 22:17  zifanwang  阅读(8)  评论(0编辑  收藏  举报  来源