Best Edge Weight
题目大意:n个点和m条边,要求求每一条边的最大权值使得这条边出现在任意的生成树中。
解题思路:先做最小生成树求得任意的一个最小生成树。然后对于在生成树上的边和不在
生成树上的边分两种情况讨论。
·1.对于不在生成树上的边u-v,如果它想要出现在任意一个生成树中,它需要满足生成树上小于u-v的路径边权的最大值。
因为按照克鲁斯卡尔的思想,我们按照边权排序,我们只需要把在u,v节点联通之前把这条变加进去就可以了。
2。对于在生成树上的边u-v,如果u-v不想被别的边所替代,那么u-v必定是小于所有不在生成树上的边且经过u-v的边的最小边权。
以上两种方法设计到对两点之间路径上边权的查询。
可以用树链剖分,也可以用倍增。
值得注意的是,对于第二种处理,最后还有再求一次,相当于线段树的pushdown操作,很巧妙。
学到了倍增法查询两点之间路径的边权最小值。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define sca(x) scanf("%d",&x)
#define pb(x) push_back(x)
#define mp(x,y) make_pair(x,y)
#define fir first
#define sec second
#define inf 0x3f3f3f3f
const int N = 2e5+5;
struct node
{
int x,y,w,id;
friend bool operator <(node a,node b)
{
return a.w<b.w;
}
};
vector<node>V;
vector<pair<int,int> >G[N];
int f[N],used[N];
int Find(int x)
{
return f[x]==x?x:f[x]=Find(f[x]);
}
void make_tree(int n,int m)//先求得任意一个最小生成树
{
for(int i=1;i<=n;i++)f[i]=i;
sort(V.begin(),V.end());
for(int i=0;i<m;i++)
{
int f1=Find(V[i].x);
int f2=Find(V[i].y);
if(f1!=f2)
{
f[f1]=f2;
G[V[i].x].pb(mp(V[i].y,V[i].w));
G[V[i].y].pb(mp(V[i].x,V[i].w));
used[i]=1;
}
}
}
int b[N][20],a[N][20],c[N][20];
int ans[N],dep[N];
void dfs(int u,int fa)
{
b[u][0]=fa;
dep[u]=dep[fa]+1;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].fir;
int w=G[u][i].sec;
if(v!=fa)
{
a[v][0]=w;//架空根节点,将边权转化为点权
dfs(v,u);
}
}
}
void pre_work(int n)
{
for(int i=1;i<=18;i++)
{
for(int j=1;j<=n;j++)
{
b[j][i]=b[b[j][i-1]][i-1];
a[j][i]=max(a[j][i-1],a[b[j][i-1]][i-1]);
}
}
}
int query(int x,int y)
{
int ans=0;
if(dep[x]<dep[y])swap(x,y);
for(int i=18;i>=0;i--)
{
if(dep[b[x][i]]>=dep[y])
{
ans=max(ans,a[x][i]);
x=b[x][i];
}
}
if(x==y)return ans;
for(int i=18;i>=0;i--)
{
if(b[x][i]!=b[y][i])
{
ans=max(ans,max(a[x][i],a[y][i]));
x=b[x][i];
y=b[y][i];
}
}
return max(ans,max(a[x][0],a[y][0]));
}
void upd(int x,int y,int val)
{
if(dep[x]<dep[y])swap(x,y);
for(int i=18;i>=0;i--)
{
if(dep[b[x][i]]>=dep[y])
{
c[x][i]=min(c[x][i],val);
x=b[x][i];
}
}
if(x==y)return ;
for(int i=18;i>=0;i--)
{
if(b[x][i]!=b[y][i])
{
c[x][i]=min(c[x][i],val);
c[y][i]=min(c[y][i],val);
x=b[x][i];
y=b[y][i];
}
}
c[x][0]=min(c[x][0],val);//最后不要忘了更新。
c[y][0]=min(c[y][0],val);
}
int main()
{
int n,m;
sca(n),sca(m);
for(int i=1;i<=m;i++)
{
node tmp;
scanf("%d%d%d",&tmp.x,&tmp.y,&tmp.w);
tmp.id=i;
V.pb(tmp);
}
make_tree(n,m);
dfs(1,0);
pre_work(n);
memset(c,inf,sizeof(c));
for(int i=0;i<m;i++)
{
if(!used[i])
{
ans[V[i].id]=query(V[i].x,V[i].y)-1;
upd(V[i].x,V[i].y,V[i].w);
}
}
for(int i=18;i>=1;i--)//由于上边的upd操作我们只更新了大区间,现在要下推。
{
for(int j=1;j<=n;j++)
{
c[j][i-1]=min(c[j][i-1],c[j][i]);
c[b[j][i-1]][i-1]=min(c[b[j][i-1]][i-1],c[j][i]);
}
}
for(int i=0;i<m;i++)
{
if(used[i])
{
int x=V[i].x;
int y=V[i].y;
if(b[x][0]==y) ans[V[i].id]=c[x][0]-1;
else ans[V[i].id]=c[y][0]-1;
}
}
for(int i=1;i<=m;i++)
{
if(ans[i]>=1000000001)printf("-1 ");
else printf("%d ",ans[i]);
}
printf("\n");
}