1 条题解
-
0
自动搬运
来自洛谷,原作者为

Meteorshower_Y
向盟约宣誓!搬运于
2025-08-24 22:17:39,当前版本为作者最后更新于2020-11-05 16:42:21,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
P6121
CSP前发篇题解增加一些RP看到这道题,第一眼,
思路就是跑 n 遍并查集,瞅了一眼数据(1≤N,M≤2×),if I do that,I will T 的起飞我觉得这道题的“弱化版”还是可以过得
虽然我没这么写输入的最后n行表示农场关的顺序,那我们倒过来看(从后往前)是不是可以看成农场开的顺序(2-1-4-3)
T0:全开着(1 2 3 4) T1:关了3 (1 2 4) T2:关了4 (1 2) T3:关了1 (2) T4:关了2 (-)看作
T4:全关着(-) T3:开了2 (2) T2:开了1 (1 2) T1:开了4 (1 2 4) T0:开了3 (1 2 3 4)1.整体思路
也就是说我们可以从搜索到时刻,每次判断加上一个点,还是否与其他点联通。
2.理论支持
把 个点和并成一个联通图,
一次合并两个点(即在两个点间加一条边),
不重复操作(两个点已经联通的就不加了),
我们只需要操作 次(加 条边)即可,
如果说我们操作不够 次(边数小于 ),
那么这张图一定不连通。我们需要依靠并查集来实现,
那用并查集来解释就是所有的点是否在同一个集合,
如果在,则联通; 反之,则不连通。3.具体实现
我们用一个数组 来存他的祖先节点,
开始初始化成每个点自己在一个集合,
如果他们不在一个集合的话,并且有一条边连接他们两个,就把他,们合并起来,操作数 .
如果当前有 个农场是开着的,记录合并的次数,如果次数等于 则YES;反之,则NO.初始化
inline void init(){ for(register int i=1;i<=n;i++) f[i]=i; }链式前向星存边
int tot,head[200010]; inline void add_edge(int from,int to){ e[++tot].from=from; e[tot].to=to; e[tot].next=head[from]; head[from]=tot; …… …… …… scanf("%d%d",&u,&v); add_edge(u,v); add_edge(v,u); }并查集(路径压缩+合并)
inline int _find(int x){ while(x!=f[x]) x=f[x]=f[f[x]]; return x; …… …… …… int fx=_find(t[i]),fy=_find(e[j].to); if(fx!=fy) { ++k; //合并次数+1 f[fx]=fy; }完整代码附上~
#include<iostream> #include<cstdio> using namespace std; struct _edge{ int from; int to; int next; }e[400010]; int tot,head[200010],k; bool vis[200010]; //判断农场是否开着 inline void add_edge(int from,int to){ //链式前向星存图 e[++tot].from=from; e[tot].to=to; e[tot].next=head[from]; head[from]=tot; } int n,m,u,v,t[200010],ans[200010],f[200010]; inline int _find(int x){ //查询+路径压缩 while(x!=f[x]) x=f[x]=f[f[x]]; return x; } inline void init(){ //并查集初始化 for(register int i=1;i<=n;i++) f[i]=i; } int main() { scanf("%d%d",&n,&m); for(register int i=1;i<=m;i++) { scanf("%d%d",&u,&v); add_edge(u,v); //无向图双向存边 add_edge(v,u); } for(register int i=1;i<=n;i++) scanf("%d",&t[i]); //农场关的时间,反着看就是开的时间 init(); //定义了函数……记得用 vis[t[n]]=1; //t_n时开了农场 t[n],标记 ans[n]=1; //只开了一个时图必定是联通的 for(register int i=n-1;i>=1;i--) //反着搜,第i 时刻开了农场t[i] { vis[t[i]]=1; //标记 for(register int j=head[t[i]];j;j=e[j].next) { if(vis[e[j].to]==1) //如果该边终点农场也开了,执行如下语句 { int fx=_find(t[i]),fy=_find(e[j].to);//并查集查询 if(fx!=fy) //不在同一个集合的话 { ++k; //合并次数 +1 f[fx]=fy; //合并两个集合 } } } if(k==n-i) ans[i]=1; //当前开了n-(i-1)个农场,判断是否连通,储存答案 else ans[i]=0; } for(register int i=1;i<=n;i++) { if(ans[i]==1) printf("YES\n"); //打印答案 else printf("NO\n"); } return 0; }若有地方没看懂可以私信我
若写的不好还请大家的体谅
若有错的地方还望大家指出
谢谢各位的支持~ 最后,Meteorshower_Y在这里祝福大家RP++,Score++
- 1
信息
- ID
- 5142
- 时间
- 2000ms
- 内存
- 250MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者