[USACO15DEC]最大流Max Flow——[树链剖分]

【题目描述】

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

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

【样例输入】
5 10
3 4
1 5
4 2
5 4
5 4
5 4
3 5
4 3
4 3
1 3
3 5
5 4
1 5
3 4

【样例输出】

9

【题意分析】

给你一棵树,每次操作就是把树上一点到一点的路径都加上一

很容易想到树链剖分(或者树上差分),蒟蒻不会树上差分,于是就打了个暴力树剖上去。这个树剖操作只有建立和路径修改。

对于每一条指令,将树上起始点到结束点路径上的节点都加1。

这位大佬用的是树上差分,也可以学一学,代码比树剖更加简洁。

传送门:(https://blog.csdn.net/ModestCoder_/article/details/81347876)

再关注一下需要输出的答案:受到压力最大的储间,那么我们在进行修改的时候,构建一个区间最大值的线段树即可。

Code:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define MAX 500000
using namespace std;

struct Front_Link_Star{
    int next,to;
}edge[MAX];

int tree[MAX << 2],lazy[MAX << 2],head[MAX],id[MAX],father[MAX];
int son[MAX],top[MAX],depth[MAX],size[MAX],cnt,dfn,ans,n,m;

inline void Add_Edge(int u,int v){
    edge[++cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt;
}

inline int read(){
    int s=0,w=1;
    char ch=getchar();
    while (!isdigit(ch)){if (ch=='-')w=-1;ch=getchar();}
    while (isdigit(ch)){s=(s << 3)+(s << 1)+ch-'0';ch=getchar();}
    return s*w;
}

inline void push_down(int now){
    lazy[now << 1]+=lazy[now];
    lazy[now << 1|1]+=lazy[now];
    tree[now << 1]+=lazy[now];
    tree[now << 1|1]+=lazy[now];
    lazy[now]=0;
}  //标记下传

inline void update(int now,int tl,int tr,int left,int right){
    if (tl>right||tr<left)return;
    if (left<=tl&&tr<=right){
        ++lazy[now];
        ++tree[now];
        push_down(now);
        return;
    }
    if (lazy[now])push_down(now);
    int mid=(tl+tr) >> 1;
    update(now << 1,tl,mid,left,right);
    update(now << 1|1,mid+1,tr,left,right);
    tree[now]=max(tree[now << 1],tree[now << 1|1]);
} //线段树

inline void Modify_Range(int x,int y){
    while (top[x]!=top[y]){
        if (depth[top[x]]<depth[top[y]])swap(x,y);
        update(1,1,n,id[top[x]],id[x]);
        x=father[top[x]];
    }
    if (depth[x]>depth[y])swap(x,y);
    update(1,1,n,id[x],id[y]);
}

inline void DFS1(int now,int fa,int d){
    father[now]=fa;
    depth[now]=d;
    size[now]=1;
    int maxson=-1;
    for (register int i=head[now];i;i=edge[i].next){
        int v=edge[i].to;
        if (v==fa)continue;
        DFS1(v,now,d+1);
        size[now]+=size[v];
        if (size[v]>maxson){
            maxson=size[v];
            son[now]=v;
        }
    }
}

inline void DFS2(int now,int top_heavy){
    top[now]=top_heavy;
    id[now]=++dfn;
    if (!son[now])return;
    DFS2(son[now],top_heavy);
    for (register int i=head[now];i;i=edge[i].next){
        int v=edge[i].to;
        if (v!=father[now]&&v!=son[now])DFS2(v,v);
    }
}   //树链剖分基本操作

int main(){
    n=read();m=read();
    for (register int i=1;i<=n-1;i++){
        int x=read(),y=read();
        Add_Edge(x,y);
        Add_Edge(y,x);
    }
    DFS1(1,0,1);
    DFS2(1,1);
    while (m--){
        int x=read(),y=read();
        Modify_Range(x,y);  //路径修改
    }
    printf("%d",tree[1]);
    return 0;
} 
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值