CF609E:Minimum spanning tree for each edge题解


题目链接:CF:Minimum spanning tree for each edge

题解:他要求我们求包含第i条边的树的最小值,我们可以先跑一遍最小生成树,把最小生成树求出来,然后再进行加边删边处理。

当我们求出一个最小生成树后,假如再连边后肯定会形成一个自环,我们考虑在这个自环上删边(不是真删,而是输出减去其权值后的 ans )。

比如在该图中,我们连接4,5两点,这三点会与其 LCA 成顶点形成自环 ,黄色那条边肯定是不能动的,要动的就是4-2和5-2这两条边,我们在处理 LCA 时变换一下,改为求解其从x点到其 LCA 的所有连边上的最大值。我们考虑树上倍增,定义一数组 walth[i][j] 为从第i点开始向上跳 2^j 个点后第i点与第 2^j 点的连边中的最大值,然后用类似于求 LCA 的做法进行处理最终求得一条链边上的 max 值。

具体实现如下:

#include
#include 
#include
#include
#define Maxn 200005
using namespace std;
typedef long long ll;
//此部分用于树上倍增 
ll head[Maxn*2],lg[Maxn*4];
struct acb{
    ll next,to,w;
}tree[Maxn*2];
ll cut;
void Add(ll u,ll v,ll w){
    tree[++cut].to=v;
    tree[cut].next=head[u];
    tree[cut].w=w;
    head[u]=cut;
}
//
ll anc[Maxn][30],dept[Maxn],walth[Maxn][30];
ll n,m,fa[Maxn],top,len; 
bool bin[Maxn];
struct abc{
    ll u,v,w,ty,id;
}edge[Maxn*2];
bool cmp(abc x,abc y){
    return x.wdept[y]){
        maxx=max(maxx,walth[x][lg[dept[x]-dept[y]]-1]);//万一两点重合了,我们就只需要一条边上的最大值 
        //哪怕它们没重合,我们仍然需要这一点在往上跳的途中的最大值 
        x=anc[x][lg[dept[x]-dept[y]]-1];//注:顺序不要搞反了 
    }
    if(x==y) return maxx;
    for(int i=lg[dept[x]]-1;i>=0;i--){
        if(anc[x][i]!=anc[y][i]){
            maxx=max(maxx,walth[x][i]); //两点没有重合,那就需要我们两边一起求取最大值 
            maxy=max(maxy,walth[y][i]);
            x=anc[x][i];
            y=anc[y][i];
            
        }
    } 
    maxx=max(walth[x][0],maxx);//注意,我们最后求到的x值是LCA的第一级儿子,还有一条边(LCA与其第一级儿子的连边)没算 
    maxy=max(walth[y][0],maxy);
    return max(maxx,maxy);
}
bool check(ll x){
    if(bin[x]) return true;
    else return false;
} 
ll work(ll x){
    ll res=len;
    if(check(x))return res;//假如这一条边在最小生成树中,那么我们就不处理 
    else res+=edge[x].w; //否则,我们预先加上目标的那一条边的权值 
    ll u=edge[x].u;
    ll v=edge[x].v;
    ll lens=LCA(u,v);
    return res-lens;
}
//以上为代码实践 
int main(){
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;i++)
        fa[i]=i;
    for(ll i=1;i<=m;i++){
        ll u,v,w;
        edge[i].id=i;
        scanf("%lld%lld%lld",&u,&v,&w);
        add(u,v,w);
    }
    len=kruskal();
    sort(edge+1,edge+m+1,rbq);//注:在求完最小生成树后一定要把数组纠正回来,因为题目要的是原装边加上并处理后的值 
    lg[1]=1;
    for(int i=2;i<=Maxn;i++)
        lg[i]=lg[i-1]+((1<