专栏文章
题解:P13548 [OOI 2022] Air Reform
P13548题解参与者 2已保存评论 1
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @minlto2d
- 此快照首次捕获于
- 2025/12/02 04:31 3 个月前
- 此快照最后确认于
- 2025/12/02 04:31 3 个月前
思路
神仙题。
首先发现 两点间的路径边权最大值的最小值,其实就是最小生成树上 的路径边权最大值。所以我们现在考虑求 的补图 的最小生成树。
考虑在图 上做 Kruskal 的过程。加入一条最小生成树上的边 时,补图中 那一侧的点(即当前在原图中和 联通的点)会形成若干联通块 , 那一侧的点会形成若干联通块 。给新图中每个联通块一个编号 ,那么这个联通块中的点为 。
枚举 中的联通块 ,枚举 中的联通块 ,那么这两个联通块能合并当且仅当对于 ,存在 在原图中没有这条边。
暴力枚举这个 ,找到合法的相当于在补图最小生成树中加入 ,且将 这两个联通块合并起来。
具体的,我们在合并的过程中先只管 会将 内部那些联通块合并起来,枚举完所有的 后再将 中的联通块和 合并。
对于 ,合并两个联通块只会让 合并(启发式合并维护)。而我们在最后将 合并到 ,所以我们需要 中每个联通块和 中的哪个联通块合并,由于 上会合并很多次,所以需要并查集。
对于 ,记录当前这个 与 中哪些联通块合并为 ,显然 中的联通块在 应该只剩下一个。若 不与其中任何一个发生合并,就在枚举完 中的所有元素后在 中加入 。
分析一下复杂度,忽略启发式合并,瓶颈在于枚举 。分两个部分:
- 当我枚举到一条不存在的边时可以直接退出枚举的过程。每次合并都是有效的,最多合并 次,所以这部分时间复杂度是 的。
- 若我枚举到一条存在的边,而这两个联通块以后一定会在同侧出现,不会再枚举到这条边。所以每条出现过的边只会被枚举一次,这部分时间复杂度是 的。
同阶,则建立补图最小生成树复杂度为 。
然后就只剩下一个树上路径最大值,这个十分简单,可以用你喜欢的数据结构处理(下面给出的代码中使用倍增)。
时间复杂度 。
代码
CPP#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
int n,m,U[N],V[N],f[N],F[N];
int find(int x)
{
if(f[x]==x) return x;
return f[x] = find(f[x]);
}
int find2(int x)
{
if(F[x]==x) return x;
return F[x] = find2(F[x]);
}
vector<int> vec[N],vec2[N];
struct node{
int u,v,w;
inline friend bool operator < (node x,node y){return x.w<y.w;}
}e[N];
map<pair<int,int>,int> mp;
int cnt,head[N],nxt[N<<1],to[N<<1],g[N<<1];
inline void add(int u,int v,int w)
{
nxt[++cnt] = head[u];
head[u] = cnt;
to[cnt] = v,g[cnt] = w;
}
int fa[N][20],mx[N][20],dep[N];
void dfs(int u,int Fa)
{
dep[u] = dep[Fa]+1;
for(int i = head[u];i;i = nxt[i])
{
int v = to[i],w = g[i];
if(v==Fa) continue;
dfs(v,u);
fa[v][0] = u,mx[v][0] = w;
}
}
inline int ask(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
int res = 0;
for(int i = 19;~i;i--)
if(dep[fa[x][i]]>=dep[y])
res = max(res,mx[x][i]),x = fa[x][i];
if(x==y) return res;
for(int i = 19;~i;i--)
if(fa[x][i]!=fa[y][i])
res = max({res,mx[x][i],mx[y][i]}),x = fa[x][i],y = fa[y][i];
return max({res,mx[x][0],mx[y][0]});
}
inline void solve()
{
cin>>n>>m;
mp.clear();
for(int i = 1;i<=m;i++)
cin>>e[i].u>>e[i].v>>e[i].w,mp[{e[i].u,e[i].v}] = mp[{e[i].v,e[i].u}] = i,U[i] = e[i].u,V[i] = e[i].v;
for(int i = 1;i<=n;i++) F[i] = f[i] = i,head[i] = 0;
cnt = 0;
for(int i = 1;i<=n;i++)
{
vec[i].clear(),vec2[i].clear();
vec[i].push_back(i),vec2[i].push_back(i);
}
sort(e+1,e+m+1);
for(int i = 1;i<=m;i++)
{
int u = find(e[i].u),v = find(e[i].v),w = e[i].w;
if(u==v) continue;
for(auto i:vec[u])
{
int fir = 0;
vector<int> tmp;
for(auto j:vec[v])
{
bool fl = 0;
for(auto x:vec2[i])
{
for(auto y:vec2[j])
if(!mp.count({x,y}))
{
add(x,y,w);
add(y,x,w);
fl = 1;
break;
}
if(fl) break;
}
if(fl)
{
if(!fir) tmp.push_back(F[i] = fir = j);//只留下第一个
else
{
F[j] = fir;
if(vec2[j].size()>vec2[fir].size()) vec2[fir].swap(vec2[j]);
for(auto k:vec2[j]) vec2[fir].push_back(k);
vec2[j].clear();
}
}
else tmp.push_back(j);
}
vec[v].swap(tmp);
}
for(auto i:vec[u])
if(find2(i)==i) vec[v].push_back(i);
else
{
int x = find2(i);
if(vec2[i].size()>vec2[x].size()) vec2[x].swap(vec2[i]);
for(auto k:vec2[i]) vec2[x].push_back(k);
vec2[i].clear();
}
f[u] = v;
}
dfs(1,0);
for(int j = 1;j<20;j++)
for(int i = 1;i<=n;i++)
fa[i][j] = fa[fa[i][j-1]][j-1],mx[i][j] = max(mx[i][j-1],mx[fa[i][j-1]][j-1]);
for(int i = 1;i<=m;i++)
cout<<ask(U[i],V[i])<<' ';
cout<<'\n';
}
signed main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T,subid;cin>>T>>subid;
while(T--) solve();
return 0;
}
相关推荐
评论
共 1 条评论,欢迎与作者交流。
正在加载评论...