[abc302f] Merge Set

发布时间 2023-10-10 16:22:32作者: LuoyuSitfitw

F - Merge Set

显然要建图

首先,我们有一个粗略的想法,对于同一集合\(S_i\)内的元素,\(S_{i,j}\)\(S_{i,j+1}\)间连一条无向的标号为\(i\)的边

那么题目显然是要我们跑最短路,若到达\(x\)的边为\(i\),然后从\(x\)向外走到点\(y\),走的边若还为\(i\),那么代价为\(0\),否则代价为\(1\)

也就是说,换边走需要\(1\)的贡献

所以考虑用集合\(S_i\)连向所有的\(S_{i,j}\),因为不换边代价为\(0\),所以\(S_i\rightarrow S_{i,j}\)的权值为\(0\),而换边走代价为\(1\),也就意味着若当前边的类型为\(S_i\),且当前点为\(S_{i,p}\),那么我们要换另一种类型的边走,也就是要从\(S_{i,p}\)走到\(S_j\)\(S_{i,j}\in S_j\)),这时需要\(1\)的代价,所以\(S_{i,j}\rightarrow S_i\)的权值为\(1\)

那么只需要建立一个起点\(S\)连向所有包含了1的集合,终点\(T\)就是M\(S\)\(T\)的最短路就是所求的答案

#include<bits/stdc++.h>
using namespace std;
const int N=4e5+5,M=5e5+5,INF=1e9;
int n,m,S,T;
int dis[N];
bool vis[N];
int head[N],cnt=1;
struct node{
	int nxt,v,val;
}tree[(M<<1)+(N>>1)];
void add(int u,int v,int val){
	tree[++cnt]={head[u],v,val},head[u]=cnt;
}
queue<int> q;
void solve(){
	for(int i=1;i<=T;++i) dis[i]=INF; 
	q.push(S);
	while(q.size()){
		int u=q.front(); vis[u]=false,q.pop();
		for(int i=head[u],v;i;i=tree[i].nxt)
			if(dis[v=tree[i].v]>dis[u]+tree[i].val){
				dis[v]=dis[u]+tree[i].val;
				if(!vis[v]) q.push(v),vis[v]=true;
			}
	}
}
int main(){
	scanf("%d%d",&n,&m),S=n+m+1,T=n+m;
	for(int i=1,a,x;i<=n;++i){
		scanf("%d",&a);
		while(a--){
			scanf("%d",&x),add(i,x+n,0),add(x+n,i,1);
			if(x==1) add(S,i,0);
		}
	}
	solve();
	if(dis[T]==INF) printf("-1\n");
	else printf("%d\n",dis[T]);

	return 0;
}