一、题目描述:
给你 $n$ 个集合 ,第 $i$ 个集合有 $A_i$ 个数,集合里的数都小于等于 $m$。
你可以选择两个至少有一个相同元素的集合,生成它们的并集,然后这两个集合消失。
求最少多少次合并之后,数字 $1$ 和 $m$ 在同一个集合中。如果不可能,请输出 $-1$ 。
数据范围:$1\leq n,m\leq 2\times 10^5,\sum_{i=1}^{n}A_i\leq 5\times 10^5$ 。
二、解题思路:
很明显这是一个最短路的题,比赛的时候也是一眼就看出来了。
但是比赛花了一个小时都没调出来,因为建图没建好。
实际上自己画一个图形式就很明了了,数字与集合之间连边,权值为 $1$ 。
然后直接跑最短路,时间复杂度 $O((n+m)log_2^{\sum_{i=1}^{n}A_i})$ ,实际运行跑的很快。
三、完整代码:
1 #include<iostream> 2 #include<queue> 3 #define N 400010 4 #define M 500010 5 using namespace std; 6 int n,m,num,x; 7 int dis[N],vis[N]; 8 //n个集合,m个点 9 //前 n 个点表示集合,后 m 个点表示点 10 struct EDGE{ 11 int v,nxt; 12 }edge[M*2]; 13 int head[N],cnt; 14 void add(int u,int v) 15 { 16 edge[++cnt].v=v; 17 edge[cnt].nxt=head[u]; 18 head[u]=cnt; 19 } 20 struct Node{ 21 int pos,val; 22 bool operator < (const Node &t)const{ 23 return t.val<val; 24 } 25 }; 26 priority_queue <Node> q; 27 void dij(int s) 28 { 29 dis[s]=0; 30 q.push({s,0}); 31 while(!q.empty()) 32 { 33 Node t=q.top(); 34 int u=t.pos,v=t.val;q.pop(); 35 if(vis[u])continue;vis[u]=1; 36 for(int i=head[u];i!=-1;i=edge[i].nxt) 37 if(dis[u]+1<dis[edge[i].v]) 38 { 39 dis[edge[i].v]=dis[u]+1; 40 q.push({edge[i].v,dis[edge[i].v]}); 41 } 42 } 43 } 44 int main() 45 { 46 cin>>n>>m; 47 for(int i=1;i<=n+m;i++) 48 head[i]=-1,dis[i]=1e9; 49 for(int i=1;i<=n;i++) 50 { 51 cin>>num; 52 for(int j=1;j<=num;j++) 53 { 54 cin>>x; 55 add(i,x+n); 56 add(x+n,i); 57 } 58 } 59 dij(n+1); 60 if(dis[n+m]==1e9) cout<<-1<<'\n'; 61 else cout<<(dis[n+m]-2)/2<<'\n'; 62 return 0; 63 }
四、写题心得:
明明是一眼题,比赛的时候却没写出来!!!只有 $1230$ 名 ,要是写出来说不定直接就绿名了。不过没关系,加油吧!拜拜!
浙公网安备 33010602011771号