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;
}