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

zzwdsj
1+2不等于3!搬运于
2025-08-24 21:51:24,当前版本为作者最后更新于2025-02-08 11:10:12,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
思路
两个结论:
- 异色点对的最短距离一定是一条边。 证明:当一条路径上的边两端颜色均相同是,这条路径两端颜色也相同,所以如果一条路径上两端颜色不同,那么这条路径上必有一条边两端颜色不同。那么如果异色点对的最短距离是一条(边数大于一的)路径,那么选这条路径上的一条两端颜色不同的边肯定比选这条路径更优。
- 异色点对的最短距离一定是一条最小生成树上的边。 证明:如果一条两端异色的边 没有被加入最小生成树。 到 必有一条只经过最小生成树上边的路径,且这条路径上的边权均不大于 的边权,与 形成环。已知点 与点 异色,那么 到 只经过最小生成树上边的路径上必有一条两端异色的边 。 的边权比 小,所以选 (最小生成树上的边)一定更优。
设 为最小生成树上点 的儿子中颜色为 的儿子连接点 的边权构成的可重集; 为任意 ( 不为空, 的颜色不为 ) 中的最小值构成的可重集; 为任意 ( 不为空) 中的最小值最小值构成的可重集。
建树时。先跑一遍 Kruskal,求出原图的最小生成树,只保留生成树上的边。然后跑 dfs 按照定义求出 , 和 ,同时记下每个点的父亲记作 ,每个点与父亲相连的边的边权记作 ,每个点的颜色记作 。
将点 的颜色修改为 时,进行一下操作以更新 , 和 :
- 更新 :由于在下面修改了 , 可能有新的最小值,应该删掉原本的最小值。在 中删除 的最小值。
- 当 与 异色时,更新 :由于在操作 3 中更新了 , 可能有新的最小值,而当 与 异色时 的最小值对 有贡献,应该删掉原本的最小值。在 中删除 的最小值(此时直到更新 前 仍是修改前 的颜色)。
- 更新 :由于修改颜色后, 颜色改变了,按照 的定义,,不在 里。在 中删除 。
- 当 与 异色时,更新 :同 2,由于在操作 3 中更新了 , 可能有新的最小值。而当 与 异色时 的最小值对 有贡献,应该加上现在的最小值。在 中插入 的最小值(此时直到更新 前 仍是修改前 的颜色)。
- 更新 :由于在 6 和 8 中修改了 , 可能有新的小值,应该删掉原本的最小值。在 中删除 的最小值。
- 更新 :由于修改颜色后, 的颜色可能不为 ,那么 的子节点中颜色为 的节点可能可以对 产生贡献。在 中插入 的最小值。
- 更新 : 将 更新为 。
- 更新 :由于修改颜色后, 的儿子中与 同色的儿子不能对 产生贡献,但之前考虑了它的贡献。在 中删除 的最小值。(此后的 为 修改后的颜色)
- 更新 :同 5,由于在 6 和 8 中修改了 , 可能有新的小值,应该加上现在的最小值。在 插入 的最小值。
- 当 与 异色时,更新 :由于在 11 中修改了 , 可能有新的小值,应该删掉原本的最小值。在 中删除 的最小值。
- 更新 :由于修改颜色后, 颜色改变了,按照 的定义,,在 里。在 中插入 。
- 当 与 异色时,更新 :由于在 11 中修改了 , 可能有新的小值,应该加上现在的最小值。在 插入 的最小值。
- 更新 :由于在上面修改了 , 可能有新的最小值,应该加上现在的最小值。在 插入 的最小值。
code
#include<bits/stdc++.h> using namespace std; int n,m,k,q,v[200005],l[200005],fa[200005]/*用于存储并查集中每个节点的父亲*/,f[200005],x,y; vector<pair<int,int> >mp[200005]; multiset<int>dis[200005],ans; unordered_map<int,multiset<int> >minn[200005]; //定义见上文 struct edge { int x,y,l; bool operator<(const edge t)const{return l<t.l;} }e[200005]; int find(int x) { if(fa[x]!=x)fa[x]=find(fa[x]); return fa[x]; } //并查集的实现 void dfs(int x,int fa) { f[x]=fa; for(pair<int,int> i:mp[x])if(i.first!=fa) minn[x][v[i.first]].insert(i.second),l[i.first]=i.second,dfs(i.first,x); for(auto i:minn[x])if(i.first!=v[x])dis[x].insert(*i.second.begin()); if(dis[x].size())ans.insert(*dis[x].begin()); } //按照定义求出 ans,minn[i][j]和dis[i] int main() { scanf("%d%d%d%d",&n,&m,&k,&q); for(int i=1;i<=m;i++)scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].l); for(int i=1;i<=n;i++)scanf("%d",&v[i]); sort(e+1,e+m+1),iota(fa+1,fa+n+1,1);//此函数的功能是把fa[1],fa[2],...,fa[n]依次赋值为1,2,...,n。 for(int i=1,cnt=0;i<=m&&cnt<n-1;i++) if(find(e[i].x)!=find(e[i].y)) fa[find(e[i].x)]=find(e[i].y),mp[e[i].x].push_back({e[i].y,e[i].l}),mp[e[i].y].push_back({e[i].x,e[i].l}),cnt++; //Kruskal 的过程 dfs(1,0); while(q--) { scanf("%d%d",&x,&y); if(f[x]) { if(dis[f[x]].size())ans.erase(ans.find(*dis[f[x]].begin()));//见上文操作1 if(v[x]!=v[f[x]])dis[f[x]].erase(dis[f[x]].find(*minn[f[x]][v[x]].begin()));//见上文操作2 minn[f[x]][v[x]].erase(minn[f[x]][v[x]].find(l[x]));//见上文操作3 if(v[x]!=v[f[x]]&&minn[f[x]][v[x]].size())dis[f[x]].insert(*minn[f[x]][v[x]].begin());//见上文操作4 } if(dis[x].size())ans.erase(ans.find(*dis[x].begin()));//见上文操作5 if(minn[x][v[x]].size())dis[x].insert(*minn[x][v[x]].begin());//见上文操作6 v[x]=y;//见上文操作7 if(minn[x][v[x]].size()&&dis[x].size())dis[x].erase(dis[x].find(*minn[x][v[x]].begin()));//见上文操作8 if(dis[x].size())ans.insert(*dis[x].begin());//见上文操作9 if(f[x]) { if(v[x]!=v[f[x]]&&minn[f[x]][v[x]].size())dis[f[x]].erase(dis[f[x]].find(*minn[f[x]][v[x]].begin()));//见上文操作10 minn[f[x]][v[x]].insert(l[x]);//见上文操作11 if(v[x]!=v[f[x]])dis[f[x]].insert(*minn[f[x]][v[x]].begin());//见上文操作12 if(dis[f[x]].size())ans.insert(*dis[f[x]].begin());//见上文操作13 } printf("%d\n",*ans.begin()); } return 0; }
- 1
信息
- ID
- 1112
- 时间
- 2000ms
- 内存
- 500MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者