校内赛 codeforces 827D【最小生成树】【树链剖分】 解题报告

找不到题面!!

题意

给出一张n(<=2e5)个点 m(<=2e5)条边无向图,保证有生成树。对于每条边,给出一个最大值maxLength,咦即能够保证这条边能够出现在所有的最小生成树中,边权的最大值为maxLength(同时,其他所有边长度不变)。给出所有m个答案。如果一条边无论边权是多大,都出现在所有最小生成树中,则输出-1.

思路

既然题面中出现了最小生成树,那这就是必须的啦。所以,先最小生成树跑一下,看看那些在树上,这里就是kruscal。
分类讨论:
如果一条边不在这棵树上,那么他可选的最大权值是它连接的树上的两点间最大权值-1。这一步可以倍增。
如果一条边在这棵树上,如果连接两点间的路径包含这条边的所有边的最小权值w,那么他的最大权值就是w-1。每次将一条链上的边权与给定值取min,最后求所有边的边权。这里就是树链剖分的事情了。

代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
const int N=400000+5;
const int inf=0x7fffffff;
int n,m,val[N],id[N],rk[N];
struct Edge
{
    int v,next,w,id;
};
Edge e[2*N];
struct date
{
    int u,v,w,id;
};
date aa[N];
int ans[N],fat[N],flag[N],head[N],num;
int find(int x)
{
    return fat[x]==x?x:fat[x]=find(fat[x]);
}
void init()
{
    for (int i=1;i<=n;i++)
    fat[i]=i;
}
bool cmp(const date&a,const date&b)
{
    return a.w<b.w;
}
bool cmp_id(const date&a,const date&b)
{
    return a.id<b.id;
}
void adde(int i,int j,int w,int id)
{
    e[++num].v=j;
    e[num].next=head[i];
    e[num].w=w;
    e[num].id=id;
    head[i]=num;
}
void Krus()
{
    int cnt=0;
    init();
    sort(aa+1,aa+m+1,cmp);
    for(int i=1;i<=m;i++)
    {
        int u=aa[i].u,v=aa[i].v,w=aa[i].w;
        int p=find(u),q=find(v);
        if (p==q) continue;
        fat[p]=q;
        adde(u,v,w,aa[i].id);
        adde(v,u,w,aa[i].id);
        flag[aa[i].id]=1;
        cnt++;
        if (cnt==n-1) break;
    }
}
int dep[N],fa[N],top[N],son[N],idc,in[N],siz[N];
void dfs(int u)
{
    siz[u]=1;
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].v;
        if(v==fa[u])continue;
        fa[v]=u;
        dep[v]=dep[u]+1;
        val[v]=e[i].w;
        id[v]=e[i].id;
        dfs(v);
        siz[u]+=siz[v];
        if(siz[son[u]]<siz[v])son[u]=v;
    }
}
void dfs(int u,int tp){
    in[u]=++idc;
    rk[idc]=u;
    top[u]=tp;
    if(son[u])dfs(son[u],tp);
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].v;
        if(v==son[u]||v==fa[u])continue;
        dfs(v,v);
    }
}
struct Node
{
    int vmax,id,lz;
    Node *ls,*rs;
    Node()
    {
        lz=inf;
    }
    void update()
    {
        vmax=max(ls->vmax,rs->vmax);
    }
    void pushdown(int lf,int rg)
    {
        if(lz!=inf)
        {
            ls->lz=min(ls->lz,lz);
            rs->lz=min(rs->lz,lz);
            lz=inf;
        }
    }
};
Node *root,pool[N*2],*tail=pool;
Node *build(int lf,int rg)
{
    Node *nd=++tail;
    if(lf==rg)
    {
        nd->id=id[rk[lf]];
        nd->vmax=val[rk[lf]];
    }
    else
    {
        int mid=(lf+rg)>>1;
        nd->ls=build(lf,mid);
        nd->rs=build(mid+1,rg);
        nd->update();
    }
    return nd;
}
int query_seg(Node *nd,int lf,int rg,int L,int R)
{
    if (L<=lf&&rg<=R) return nd->vmax;
    int mid=(lf+rg)>>1,rt=0;
    if (L<=mid) rt=max(rt,query_seg(nd->ls,lf,mid,L,R));
    if (R>mid) rt=max(rt,query_seg(nd->rs,mid+1,rg,L,R));
    return rt;
}
void modify(Node *nd,int lf,int rg,int L,int R,int val)
{
    if (L<=lf&&rg<=R) {nd->lz=min(nd->lz,val);return;}
    int mid=(lf+rg)>>1;
    if (L<=mid) modify(nd->ls,lf,mid,L,R,val);
    if (R>mid) modify(nd->rs,mid+1,rg,L,R,val);
}
int query(int u,int v)
{
    int rt=0;
    while(top[u]!=top[v])
    {
        if (dep[top[u]]<dep[top[v]]) swap(u,v);
        rt=max(rt,query_seg(root,1,n,in[top[u]],in[u]));
        u=fa[top[u]];
    }
    if (u==v) return rt;
    if (dep[u]<dep[v]) swap(u,v);
    rt=max(rt,query_seg(root,1,n,in[son[v]],in[u]));
    return rt;
}
void modify(int u,int v,int w)
{
    while(top[u]!=top[v])
    {
        if (dep[top[u]]<dep[top[v]]) swap(u,v);
        modify(root,1,n,in[top[u]],in[u],w);
        u=fa[top[u]];
    }
    if (u==v) return;
    if (dep[u]<dep[v]) swap(u,v);
    modify(root,1,n,in[son[v]],in[u],w);
}
void get_ans(Node *nd,int lf,int rg)
{
    if (lf==rg)
    {
        ans[nd->id]=nd->lz;
        return;
    }
    nd->pushdown(lf,rg);
    int mid=(lf+rg)>>1;
    get_ans(nd->ls,lf,mid);
    get_ans(nd->rs,mid+1,rg);
}
int main()
{
//  freopen("tree.in","r",stdin);
//  freopen("tree.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
    scanf("%d%d%d",&aa[i].u,&aa[i].v,&aa[i].w),aa[i].id=i;
    Krus();
    sort(aa+1,aa+m+1,cmp_id);
    fa[1]=0;dep[1]=1;
    dfs(1);
    dfs(1,1);
    root=build(1,n);
    for (int i=1;i<=m;i++)
    {
        if (flag[i]) continue;
        ans[i]=query(aa[i].u,aa[i].v)-1;
        modify(aa[i].u,aa[i].v,aa[i].w-1);
    }
    get_ans(root,1,n);
    for (int i=1;i<=m;i++)
    printf(ans[i]==inf?"-1\n":"%d\n",ans[i]);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值