P3128 [USACO15DEC]Max Flow P(树链剖分+线段树维护max)

https://www.luogu.com.cn/problem/P3128


 

FJ给他的牛棚的N(2≤N≤50,000)个隔间之间安装了N-1根管道,隔间编号从1到N。所有隔间都被管道连通了。

FJ有K(1≤K≤100,000)条运输牛奶的路线,第i条路线从隔间si运输到隔间ti。一条运输路线会给它的两个端点处的隔间以及中间途径的所有隔间带来一个单位的运输压力,你需要计算压力最大的隔间的压力是多少。


思路:树链剖分后线段树操作维护区间最值,每次进行+1操作,打了tag的子树maxval+1,父亲的maxval由两个子树的最大maxval转移过来。最后直接查tree[1].maxval。

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=5e4+100;
typedef long long LL;
struct Tree{
    LL l,r,maxval,tag;
}tree[maxn*4];
LL a[maxn];
///线段树部分
void push_up(LL p){
    tree[p].maxval=max(tree[p*2].maxval,tree[p*2+1].maxval);
}
void addtag(LL p,LL d){
    tree[p].tag+=d;
    tree[p].maxval+=d;
}
void push_down(LL p){
    if(tree[p].tag!=0){
        addtag(p*2,tree[p].tag);
        addtag(p*2+1,tree[p].tag);
        tree[p].tag=0;
    }
}
void build(LL p,LL l,LL r){
    tree[p].l=l;tree[p].r=r;tree[p].tag=0;tree[p].maxval=0;
    if(l==r){return;}
    LL mid=(l+r)>>1;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    push_up(p);
}
void modify(LL p,LL l,LL r,LL d){
    if(l<=tree[p].l&&r>=tree[p].r){
        addtag(p,d);
        return;
    }
    push_down(p);
    LL mid=(tree[p].l+tree[p].r)>>1;
    if(l<=mid) modify(p*2,l,r,d);
    if(r>mid) modify(p*2+1,l,r,d);
    push_up(p);
}
LL query(LL p,LL l,LL r){
    return tree[1].maxval;
}
///树剖部分
LL son[maxn],siz[maxn],dep[maxn],fa[maxn];
LL top[maxn];
LL id[maxn],tot=0;
LL new_w[maxn];
vector<LL>g[maxn];
void predfs(LL u,LL father){
    siz[u]=1;dep[u]=dep[father]+1;
    fa[u]=father;
    for(LL i=0;i<g[u].size();i++){
        LL v=g[u][i];
        if(v==father) continue;
        predfs(v,u);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]]){
            son[u]=v;
        }
    }
}
void dfs(LL u,LL topx){
    id[u]=++tot;
    top[u]=topx;
    new_w[tot]=a[u];
    if(!son[u]) return;///叶子节点访问完了
    dfs(son[u],topx);///先处理重儿子
    for(LL i=0;i<g[u].size();i++){
        LL v=g[u][i];
        if(v==fa[u]||v==son[u]) continue;
        dfs(v,v);
    }
}
void modify_path(LL u,LL v,LL d)
{
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        modify(1,id[top[u]],id[u],1);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    modify(1,id[u],id[v],1);
}
LL query_path(LL u,LL v){
    return tree[1].maxval;
}
int main(void)
{
  cin.tie(0);std::ios::sync_with_stdio(false);
  LL n,m;cin>>n>>m;
  for(LL i=1;i<n;i++){
    LL u,v;cin>>u>>v;
    g[u].push_back(v);
    g[v].push_back(u);
  }
  predfs(1,0);
  dfs(1,0);
  build(1,1,n);
  while(m--){
    LL u,v;cin>>u>>v;
    modify_path(u,v,1);
  }
  cout<<query_path(1,n)<<endl;
return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值