CF1790G Tokens on Graph - bfs -

题目链接:https://codeforces.com/contest/1790/problem/G

题解:
首先一个硬币如果能移动到根节点,必然从他的父亲开始一直到根的路径都是bonus
考虑钦定让某个硬币移动到根,其它的硬币能否为其提供足够的步数。因为是个图,所以先用 bfs 求一下根到这个点的 路径全为 bonus 的最短路径(或者是这个点邻居的最短路径+1)
如何判断能否提供足够步数呢?首先如果 \(i\) 点和 \(i\) 点相邻的一个点都是 bonus,那么 \(j, (j,i) \in E\) 点上的硬币一定可以贡献无数次步数
否则,如果 \(i\) 点是 bonus且没有邻居是 bonus,那么 \(j, (j,i) \in E\) 只能贡献一次步数,注意贡献步数的点和这个点是不是 bonus没有关系,和邻居有关
最后判断的时候就看一下除了这个点之外是否存在 \(i\) 为无限贡献,或者只能贡献1次的 \(i\) 的个数 \(\geq\) dis-1 (因为可以一开始先让钦定的硬币移动)
\(type[i]=2\) 代表 \(i,j\) 都是bonus,\(ntype[i]=2\) 代表 \(i\) 的某个邻居是\(type=2\) ,因此 \(i\) 能贡献无数次步数

// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>

using namespace std;

typedef long long ll;
typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 2e5+5;

int n,m,p,b,dis[maxn], bonus[maxn], type[maxn], ntype[maxn];
vector<int>g[maxn];
void solve(){
	scanf("%d%d",&n,&m);
	scanf("%d%d",&p,&b);
	for(int i=1;i<=n;i++)dis[i] = -1, g[i].clear(), bonus[i] = type[i] = ntype[i] = 0;
	
	vector<int>token;
	int win = 0;
	for(int i=1;i<=p;i++){
		int x;scanf("%d",&x);
		token.push_back(x);
		if(x == 1)win = 1;
	}
	for(int i=1;i<=b;i++){
		int x;scanf("%d",&x);
		bonus[x] = 1;
	}
	for(int i=1;i<=m;i++){
		int x,y;scanf("%d%d",&x,&y);
		g[x].push_back(y), g[y].push_back(x);
	}
	
	for(int i=1;i<=n;i++){
		for(int v : g[i]){
			if(bonus[i] && bonus[v]){
				type[i] = 2;
			}
		}
	}
	for(int i : token){
		for(int j : g[i]){
			if(bonus[j])ntype[i] = 1;
			if(type[j] == 2){
				ntype[i] = 2;
				break;
			}
		}
	}
	queue<int>Q;
	Q.push(1);dis[1] = 0;

	while(!Q.empty()){
		int u = Q.front();Q.pop();
		for(int v : g[u]){
			if(bonus[v] && (dis[v] == -1)){
				dis[v] = dis[u] + 1;
				Q.push(v);
			}
		}
	}
	
	int cnt[4];memset(cnt,0,sizeof cnt);
	for(int i=1;i<=n;i++)++ cnt[ntype[i]];
	
	for(int i : token){
		-- cnt[ntype[i]];
		int res = ~dis[i] ? dis[i] : INF;
		for(int u : g[i]){
			if(~dis[u]){
				res = min(res, dis[u] + 1);
			}
		}
		if(res != INF){
			if(cnt[2] >= 1 || cnt[1] >= (res-1)){
				puts("YES");
				return ;
			}
		}
		++ cnt[ntype[i]];
	}
	puts(win ? "YES" : "NO");
}

signed main(){
//	freopen("G.in","r",stdin);
	int te;scanf("%d",&te);
	while(te--)solve(); 

	return 0;
}
posted @ 2023-01-29 11:32  SkyRainWind  阅读(47)  评论(0编辑  收藏  举报