最大流【省选模拟赛】

79 篇文章 0 订阅
33 篇文章 0 订阅

标签:树链剖分,线段树

题目

这里写图片描述

N<=1e5,M,Q<=2e5

保证询问中S,T不相等

分析

最大流就是最小割

对于M=N-1的情况,显然只需要割掉两点间权值最小的边

对于全部数据,我们发现每次最小割割掉的边只出现在

  • 所有简单路径都包含的任何一条边

  • 不同在两者间任意简单路径,但在一个环内的两条边

将每个环断成链后,用线段树维护,再将环缩点,做树链剖分

对于一次询问(S,T),先求出LCA(S,T)

将其转化为两条路径,考虑一些特殊的情况

重链直接在树剖内查询,轻边在原本的线段树内查询

如果修改的是非环边,直接在树剖内单点修改

否则在一开始建出的线段树内修改,再修改这个环在树剖内对应节点的重儿子的权值


毒瘤题考码力,硬肝了5h才AC

code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rep(i,a,b) for(register int i=a;i<=b;i++)
#define dep(i,a,b) for(register int i=a;i>=b;i--)
#define ll long long
#define mem(x,num) memset(x,num,sizeof x)
#define reg(x) for(int i=last[x];i;i=e[i].next)
#define inf 0x7fffffff
using namespace std;
inline ll read()
{
    ll f=1,x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int maxn=1e5+6;
struct node{
    node *lc,*rc;int lower;
    void update(){lower=min(lc->lower,rc->lower);}
}pool[maxn<<2],*cur=pool;
struct SegTree{//线段树指针写法
    node *root;int n;
    void modify(node *u,int ul,int ur,int p,int k){
        if(ul==ur){u->lower=k;return;}
        int mid=(ul+ur)>>1;
        if(p<=mid)modify(u->lc,ul,mid,p,k);else modify(u->rc,mid+1,ur,p,k);
        u->update();
    }
    inline int query(node *u,int ul,int ur,int l,int r){
        if(l<=ul&&ur<=r)return u->lower;
        int mid=(ul+ur)>>1,re=inf;
        if(l<=mid)re=min(re,query(u->lc,ul,mid,l,r));
        if(r>mid)re=min(re,query(u->rc,mid+1,ur,l,r));
        return re;
    }
    void build(node *&u,int ul,int ur,int *val){
        u=cur++;
        if(ul==ur){u->lower=val[ul];return;}
        int mid=(ul+ur)>>1;
        build(u->lc,ul,mid,val);
        build(u->rc,mid+1,ur,val);
        u->update();
    }
    void modify(int p,int k){modify(root,1,n,p,k);}
    inline int query(int l,int r){return l>r?inf:query(root,1,n,l,r);}
    void init(int _n,int *val){n=_n;build(root,1,n,val);}
}segt;
struct edge{int to,next,w;}e[maxn<<2];
int n,m,cnt,last[maxn],belong[maxn],st[maxn],id[maxn],num[maxn];
bool vis[maxn];int sta[maxn],len,idx,ori[maxn];
inline void insert(int u,int v,int w){e[++cnt]=(edge){v,last[u],w};last[u]=cnt;}
inline int calc(int u,int v){
    if(u==v)return inf;
    int sec=belong[u];
    if(id[u]>id[v])swap(u,v);
    return segt.query(id[u],id[v]-1)+min(segt.query(id[v],st[sec]+num[sec]-1),segt.query(st[sec],id[u]-1));
}
namespace tree{
    SegTree segt_tree;
    struct edge{int to,next,w,escape;}e[maxn<<1];
    int n,cnt,idx,last[maxn],num[maxn],val[maxn],ori[maxn];
    int dep[maxn],fa[maxn],top[maxn],pre[maxn],chain[maxn],dfn[maxn],heavy[maxn],pos[maxn];
    inline void insert(int u,int v,int w,int o){e[++cnt]=(edge){v,last[u],w,o};last[u]=cnt;}
    void dfs1(int u){
        num[u]=1;
        reg(u){
            int &v=belong[e[i].to];
            if(fa[u]==v)continue;
            fa[v]=u,dep[v]=dep[u]+1;
            top[v]=e[i].to,pre[v]=e[i].escape,val[v]=e[i].w;
            dfs1(v);
            num[u]+=num[v];
        }
    }
    void dfs2(int u,int Fa){
        pos[dfn[u]=++idx]=u,chain[u]=Fa;
        reg(u){
            int &v=belong[e[i].to];
            if(v==fa[u])continue;
            if(num[v]>num[heavy[u]])heavy[u]=v;
        }
        if(!heavy[u])return;
        dfs2(heavy[u],Fa);
        reg(u){
            int &v=belong[e[i].to];
            if(v==fa[u]||v==heavy[u])continue;
            dfs2(v,v);
        }
    }
    void init(){
        dfs1(1);dfs2(1,1);
        rep(i,1,n)
            if(heavy[fa[i]]==i&&fa[i]!=1)ori[dfn[i]]=min(val[i],calc(pre[i],top[fa[i]]));
        segt_tree.init(n,ori);
    }
    inline int getlca(int u,int v){
        while(chain[u]!=chain[v]){
            if(dep[chain[u]]>dep[chain[v]])swap(u,v);
            v=fa[chain[v]];
        }
        if(dep[u]>dep[v])swap(u,v);
        return u;
    }
    inline int getanc(int u,int Fa){
        int now=0;
        while(chain[u]!=chain[Fa])now=u,u=fa[chain[u]];
        return u==Fa?chain[now]:pos[dfn[Fa]+1];
    }
    inline int chain_query(int u,int Fa){
        int re=val[Fa];
        while(chain[u]!=chain[Fa]){
            re=min(min(re,calc(pre[chain[u]],top[fa[chain[u]]])),min(segt_tree.query(dfn[chain[u]]+1,dfn[u]),val[chain[u]]));
            u=fa[chain[u]];
        }
        re=min(re,segt_tree.query(dfn[Fa]+1,dfn[u]));
        return re;
    }
    inline int query(int u,int v){
        int Fa=getlca(belong[u],belong[v]);
        int u1=getanc(belong[u],Fa),v1=getanc(belong[v],Fa);
        int re=calc((u1?pre[u1]:u),(v1?pre[v1]:v));
        if(u1){
            re=min(re,calc(u,top[belong[u]]));
            re=min(re,chain_query(belong[u],u1));
        }
        if(v1){
            re=min(re,calc(v,top[belong[v]]));
            re=min(re,chain_query(belong[v],v1));
        }
        return re;
    }
    void modify(int u,int v,int w){
        if(belong[u]==belong[v]){
            int sec=belong[u];
            if((id[u]+1-st[sec])%(::num[sec])!=(id[v]-st[sec]))swap(u,v);
            segt.modify(id[u],w);
            int &tmp=heavy[sec];
            if(tmp&&fa[tmp]!=1)segt_tree.modify(dfn[tmp],min(val[tmp],calc(pre[tmp],top[fa[tmp]])));
        }else{
            u=belong[u],v=belong[v];
            if(dep[u]>dep[v])swap(u,v);
            val[v]=w;
            if(heavy[u]==v&&u!=1)segt_tree.modify(dfn[v],min(val[v],calc(pre[v],top[fa[v]])));
        }
    }
}//树剖板子
void getcir(int u,int Fa){
    vis[u]=true,sta[len++]=u;
    reg(u){
        int &v=e[i].to;
        if(v==Fa)continue;
        if(!vis[v]){
            getcir(v,u);
            if(belong[u]==belong[v])ori[id[v]]=e[i].w;
        }else if(!belong[v]){
            tree::n++;
            while(1){
                int tmp=sta[len-num[tree::n]-1];
                belong[tmp]=tree::n,id[tmp]=++idx;
                num[tree::n]++;
                if(tmp==v)break;
            }
            ori[id[v]]=e[i].w;st[tree::n]=id[u];
        }
    }
    if(!belong[u]){
        st[++tree::n]=id[u]=++idx;
        num[belong[u]=tree::n]=1;
    }
    len--;
}
void init(){
    n=read();int m=read();
    rep(i,1,m){
        int u=read(),v=read(),w=read();
        insert(u,v,w);insert(v,u,w);
    }
    getcir(1,0);segt.init(n,ori);
    rep(u,1,n)
        reg(u){
            int &v=e[i].to;
            if(belong[u]!=belong[v])tree::insert(belong[u],v,e[i].w,u);
        }
    tree::init();
}
void solve(){
    int T=read();
    while(T--){
        int opt=read(),u=read(),v=read();
        if(opt)tree::modify(e[u*2-1].to,e[u*2].to,v);else printf("%d\n",tree::query(u,v));
    }
}
int main()
{
    init();
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值