P4374 [USACO18OPEN]Disruption —— 树剖 + lca 存边变为存子节点

题目描述
Farmer John自豪于他所经营的交通发达的的农场。这个农场是由NN块牧场(2 \leq N \leq 50,0002≤N≤50,000)组成的,N-1N−1条双向道路将它们连接起来,每一条道路的都为一单位长度。Farmer John注意到,从任何一块牧场到另一块牧场,都能通过一组合适的道路到达。

尽管FJ的农场现在是连通的,他担心如果有一条道路被阻断会发生什么,因为这事实上会将他的农场分为两个不相交的牧场集合,奶牛们只能够在每一个集合内移动但不能在集合间移动。于是FJ又建造了MM条额外的双向道路(1 \leq M \leq 50,0001≤M≤50,000),每一条的长度都是一个至多为10^910
9
的正整数。奶牛们仍然可以使用原有的道路进行移动,除非其中的某些被阻断了。

如果某条原有的道路被阻断了,农场就会被分为两块不相交的区域,那么FJ就会从他的额外修建的道路中选择一条能够重建这两块区域的连通性的,取代原来那条,从而奶牛们又可以从任何一块牧场去往另一块牧场。

对于农场上每一条原有的道路,帮助FJ选出最短的替代用的道路。

输入输出格式
输入格式:
输入的第一行包含NN和MM。接下来的N-1N−1行,每行用整数pp和qq描述了一条原有的道路,其中p \neq qp≠q是这条道路连接的两块牧场(在1 \ldots N1…N范围内)。剩下的MM行,每行用三个整数pp、qq和rr描述了一条额外的道路,其中rr是这条道路的长度。任何两块牧场之间至多只有一条道路。

输出格式:
对原有的N-1N−1条道路的每一条,按照它们在输入中出现的顺序,输出如果这条道路被阻断的话,能够重新连接农场的最短的替代用道路的长度。如果不存在合适的替代用的道路,输出-1。

输入输出样例
输入样例#1:
6 3
1 2
1 3
4 1
4 5
6 5
2 3 7
3 6 8
6 4 5
输出样例#1:
7
7
8
5
5

题意:

给你一棵树,然后给你一些额外的边,问你依次删掉这条树的一个边,添加一条边使得这棵树连通,问你添加边的最小长度是多少,如果没有输出-1

题解:

给你这些额外的边就是说这条路径上的边都有可能是通过这个边达到最小的,那么我们就更新这个路径的最小值,那肯定就是树链剖分了,但是要注意不能更新lca的内容,因为我们是把边当做儿子节点来存,举个例子:
在这里插入图片描述
如果我们更新lca的话,删掉1 3 这条边的时候,找到的就是3的最小值 2 了,所以需要找到离lca最近的一个子节点,用倍增就ok了

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pa pair<int,int>
const int N=1e5+5;
const ll inf=1e18;
struct node
{
    int to,next;
}e[N*2];
int head[N],cnt,tim,fa[N],son[N],dep[N],top[N],en[N],siz[N],pos[N];
ll flag[N*4],minn[N*4];
int n,m,rt,mod;
void add(int x,int y)
{
    e[cnt].to=y;
    e[cnt].next=head[x];
    head[x]=cnt++;
}
int f[N][25],vis[N],depth[N],lv[N];
void bfs()
{
    f[1][0]=1;
    depth[1]=0;
    queue<int>Q;
    Q.push(1);
    while(!Q.empty())
    {
        int u,v;
        u=Q.front();
        Q.pop();
        for(int i=1;i<=16;i++)
            f[u][i]=f[f[u][i-1]][i-1];
        for(int i=head[u];~i;i=e[i].next)
        {
            v=e[i].to;
            if(v==f[u][0])
                continue;
            depth[v]=depth[u]+1;
            f[v][0]=u;
            Q.push(v);
        }
    }
}
int firstson;
int lca(int x,int y){
    if(depth[x]<depth[y])swap(x,y);
//令x为深度较深的点
    for(int i=16;i>=0;i--)if(depth[f[x][i]]>=depth[y])x=f[x][i];
//让x向上走到与y同一深度
    if(x==y)return x; //如果直接是lca,直接返回
    for(int i=16;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
//x,y同时向上走,直到父节点相同
    return f[x][0]; //返回父节点
}
void dfs1(int u)
{
    siz[u]=1;
    for (int i=head[u];~i;i=e[i].next)
    {
        int v=e[i].to;
        if (v!=fa[u])
        {
            fa[v]=u;
            dep[v]=dep[u]+1;
            dfs1(v);
            siz[u]+=siz[v];
            if (son[u]==0||siz[v]>siz[son[u]])
                son[u]=v;
        }
    }
}
void dfs2(int u,int f)
{
    tim++;
    top[u]=f;
    pos[u]=tim;
    if (son[u]) dfs2(son[u],f);
    for (int i=head[u];~i;i=e[i].next)
    {
        int v=e[i].to;
        if (v!=fa[u]&&v!=son[u])
        {
            dfs2(v,v);
        }
    }
    en[u]=tim;
}
void pushup(int root)
{
    minn[root]=min(minn[root<<1],minn[root<<1|1]);
}
void pushdown(int root)
{
    if(flag[root]==inf)
        return ;
    minn[root<<1]=min(minn[root<<1],flag[root]);
    minn[root<<1|1]=min(minn[root<<1|1],flag[root]);
    flag[root<<1]=min(flag[root<<1],flag[root]);
    flag[root<<1|1]=min(flag[root<<1|1],flag[root]);
    flag[root]=inf;
}
void add(int l,int r,int root,int ql,int qr,ll val)
{
    if(l>=ql&&r<=qr)
    {
        minn[root]=min(minn[root],val);
        flag[root]=min(flag[root],val);
        return ;
    }
    pushdown(root);
    int mid=l+r>>1;
    if(mid>=ql)
        add(l,mid,root<<1,ql,qr,val);
    if(mid<qr)
        add(mid+1,r,root<<1|1,ql,qr,val);
    pushup(root);
}
ll ask(int l,int r,int root,int q)
{
    if(l==r&&l==q)
        return minn[root];
    int mid=l+r>>1;
    pushdown(root);
    ll ans=inf;
    if(mid>=q)
        ans=min(ans,ask(l,mid,root<<1,q));
    if(mid<q)
        ans=min(ans,ask(mid+1,r,root<<1|1,q));
    return ans;
}
void update(int x,int y,ll val)
{
    while (top[x]!=top[y])
    {
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        add(1,n,1,pos[top[x]],pos[x],val);
        x=fa[top[x]];
    }
    if (pos[x]>pos[y]) swap(x,y);
    add(1,n,1,pos[x],pos[y],val);
}
/*void query(int x,int y)
{
    ll ans=inf;
    while (top[x]!=top[y])
    {
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        ans=min(ans,ask(1,n,1,pos[top[x]],pos[x]));
        x=fa[top[x]];
    }
    if (pos[x]>pos[y]) swap(x,y);
    ans=min(ans,ask(1,n,1,pos[x],pos[y]));
    printf("%lld\n",ans);
}*/
pa a[N];
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    memset(head,-1,sizeof(head));
    for(int i=1;i<N*4;i++)
        minn[i]=flag[i]=inf;
    rt=1;
    scanf("%d%d",&n,&m);
    int x,y;
    ll val;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y),add(y,x);
        a[i]={x,y};
    }
    dfs1(rt);
    dfs2(rt,rt);
    bfs();
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%lld",&x,&y,&val);
        int l=lca(x,y);
        if(x==l)
        {
            firstson=y;
            for(int i=16;i>=0;i--)if(depth[f[firstson][i]]>depth[l])firstson=f[firstson][i];
            update(firstson,y,val);
        }
        else if(y==l)
        {
            firstson=x;
            for(int i=16;i>=0;i--)if(depth[f[firstson][i]]>depth[l])firstson=f[firstson][i];
            update(firstson,x,val);
        }
        else
        {
            firstson=x;
            for(int i=16;i>=0;i--)if(depth[f[firstson][i]]>depth[l])firstson=f[firstson][i];
            update(firstson,x,val);
            firstson=y;
            for(int i=16;i>=0;i--)if(depth[f[firstson][i]]>depth[l])firstson=f[firstson][i];
            update(firstson,y,val);
        }
        //printf("%d***\n",firstson);
    }
    for(int i=1;i<n;i++)
    {
        ll ans=ask(1,n,1,dep[a[i].first]>dep[a[i].second]?pos[a[i].first]:pos[a[i].second]);
        printf("%lld\n",ans==inf?-1:ans);
        //printf("%d\n",dep[a[i].first]>dep[a[i].second]?a[i].first:a[i].second);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值