思路
- 有一个性质(我做之前也不知道, 但是现在告诉你)
- 对于一个图 G, 将他的所有边权 的边以任意顺序加入最小生成树中, 图的联通性一定
- “ 任意顺序 ” : 像 Kruskal 的流程一样, 按边权从小到大, 但是边的编号(输入时的)任意顺序
- “ 联通性一定 ” : 比如无论那种顺序加边, 将权 的权加入后, 1 和 2 一定是联通的, 4 和 5 一定不联通
- 将询问的边按照边权为第一关键字, 询问标号为第二关键字排序
- 根据上面的性质, 当前边权变为 w 时, 所有 的边都可以加入并查集(此操作无需撤销)
- 对于同一次询问的边:
- 逐条加入, 判断是否合法
- 如果加入这条边之前, 该边的两端点 u, v 就在同一个集合中, 那么非法
- 同一次询问的边处理完之后, 将上述的加边操作全部撤销, 进入下一个询问
- 这样的话我们不难发现我们把权值相同且不处在当前询问中的边后加入了,而处于当前询问中的会优先加入,那么加入是合法的可能性会更大
代码
- 如果你 WA on TEST 48
- 检查一下代码中加注释的地方
- 处理同一次询问的时候有没有判断边权是否改变 !
| # include <bits/stdc++.h> |
| # define int long long |
| using namespace std; |
| const int N = 1.5e6 + 10; |
| |
| int n, m, q; |
| int u, v, w, k; |
| int ans[N]; |
| |
| struct Edge{ |
| int u, v, w; |
| bool operator < (const Edge &p)const{ |
| return w < p.w; |
| } |
| }e[N], ee[N]; |
| int cnte; |
| |
| struct Add{ |
| int qid, eid; |
| bool operator < (const Add &p)const{ |
| if(e[eid].w == e[p.eid].w){ |
| return qid < p.qid; |
| }else{ |
| return e[eid].w < e[p.eid].w; |
| } |
| } |
| }qq[N]; |
| int cntq; |
| |
| struct UFS{ |
| int s[N], si[N], cnt; |
| int st[N], tp; |
| |
| void Insert(){ |
| ++cnt; |
| s[cnt] = cnt; |
| si[cnt] = 1; |
| } |
| |
| int Find(int x){ |
| return (x == s[x] ? x : Find(s[x])); |
| } |
| |
| void Merge(int x, int y, int ba){ |
| x = Find(x); |
| y = Find(y); |
| if(x == y){ |
| return; |
| } |
| if(si[x] < si[y]){ |
| swap(x, y); |
| } |
| s[y] = x; |
| si[x] += si[y]; |
| if(ba == 1){ |
| st[++tp] = y; |
| } |
| } |
| |
| void Delete(int x){ |
| while(tp > x){ |
| int k = st[tp]; |
| si[s[k]] -= si[k]; |
| s[k] = k; |
| tp--; |
| } |
| } |
| }s; |
| |
| signed main(){ |
| |
| ios::sync_with_stdio(0); |
| cin.tie(0); cout.tie(0); |
| |
| cin >> n >> m; |
| for(int i = 1; i <= m; i++){ |
| cin >> u >> v >> w; |
| e[i].u = ee[i].u = u; |
| e[i].v = ee[i].v = v; |
| e[i].w = ee[i].w = w; |
| } |
| |
| sort(ee + 1, ee + 1 + m); |
| cnte = 1; |
| |
| cin >> q; |
| for(int i = 1; i <= q; i++){ |
| cin >> k; |
| ans[i] = 1; |
| for(int j = 1; j <= k; j++){ |
| cin >> qq[++cntq].eid; |
| qq[cntq].qid = i; |
| } |
| } |
| |
| sort(qq + 1, qq + 1 + cntq); |
| int up = cntq; |
| cntq = 1; |
| |
| for(int i = 1; i <= n; i++){ |
| s.Insert(); |
| } |
| while(cntq <= up){ |
| w = e[qq[cntq].eid].w; |
| int qid = qq[cntq].qid; |
| if(cntq > 1 && w != e[qq[cntq - 1].eid].w || qid != qq[cntq - 1].qid){ |
| s.Delete(0); |
| } |
| while(cnte <= m && ee[cnte].w < w){ |
| s.Merge(ee[cnte].u, ee[cnte].v, 0ll); |
| cnte++; |
| } |
| while(cntq <= up && qq[cntq].qid == qid && e[qq[cntq].eid].w == w){ |
| if(s.Find(e[qq[cntq].eid].u) == s.Find(e[qq[cntq].eid].v)){ |
| ans[qid] = 0; |
| } |
| if(ans[qid]){ |
| s.Merge(e[qq[cntq].eid].u, e[qq[cntq].eid].v, 1ll); |
| } |
| cntq++; |
| } |
| } |
| for(int i = 1; i <= q; i++){ |
| if(ans[i]){ |
| cout << "YES\n"; |
| }else{ |
| cout << "NO\n"; |
| } |
| } |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话