一、题目描述:

  给你 $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$ 名 ,要是写出来说不定直接就绿名了。不过没关系,加油吧!拜拜!

posted on 2023-05-21 22:16  trh0630  阅读(28)  评论(0)    收藏  举报