P3128 [USACO15DEC]最大流Max Flow [树上差分-点差]

两点间的路径每个点给一个单位的压力,询问最大压力的点。

LCA用的离线,点在树上差分

#include<bits/stdc++.h>
#define mxn 500005
using namespace std;
int n,k,x,y,hd[mxn],fa[mxn],cnt,vis[mxn],dfn[mxn],cf[mxn],du[mxn],s,nod[mxn],bis[mxn],up[mxn];
int hk[100005];
struct Edge{
    int nxt,to,lca;
}edge[(mxn+100005)<<1];
void add(int u,int v)
{
    cnt++;
    edge[cnt].to =v;
    edge[cnt].nxt=hd[u];
    hd[u]=cnt;
}
void add_ask(int u,int v)
{
    cnt++;//边不能还是从0开始   
    edge[cnt].to=v;
    edge[cnt].nxt=hk[u];
    hk[u]=cnt;
} 
void init()
{
    int num=0;
    for(int i=1;i<=n;i++)
    {
        fa[i]=i;
        if(du[i]>num){
            num=du[i];
            s=i;
        }
    }   
    memset(vis,0,sizeof vis);
}
int find(int u)
{
    if(fa[u]==u) return u;
    return fa[u]=find(fa[u]);
} 
//离线 
//里面的并查集不压缩
int tim=0;
void tarjan(int u,int f)
{
    for(int i=hd[u];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(!vis[v] && v!=f)
        {
            up[v]=u;
            tarjan(v,u);
            fa[v]=u; 
        }
    }//u访问完了
    vis[u]=1;
    //先不更新父亲,先询问 
    for(int j=hk[u];j;j=edge[j].nxt)
    {
        int w=edge[j].to;
        if(vis[w])
        {
            //u,v的lca是find(w)
            edge[j].lca=find(w);
//          cout<<u<<","<<w<<","<<edge[j].lca<<endl;
        }
    }

    dfn[u]=tim++; 
}
int dfs(int u,int f)//类似于记忆化 
{
    int sum=0;
    if(du[u]==1) return cf[u];
    if(nod[u]) return nod[u];
    if(cf[u]) sum+=cf[u];
    for(int i=hd[u];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(v!=f)
        {
            sum+=dfs(v,u);
        }
    } 
    nod[u]=sum;//nod[4]
    return sum;
}
int p[mxn],q[mxn];
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n-1;i++)
    {
        scanf("%d%d",&x,&y);
        du[x]++,du[y]++;
        add(x,y);add(y,x);
    }
//  for(int u=1;u<=n;u++)
//  {
//      cout<<u<<","<<du[u]<<endl;
//      for(int j=hd[u];j;j=edge[j].nxt)
//      {
//          cout<<u<<","<<edge[j].to<<endl;
//      }
//  }

    for(int i=1;i<=k;i++)
    {
        scanf("%d%d",&p[i],&q[i]);
        add_ask(p[i],q[i]);add_ask(q[i],p[i]); //如何表示有询问? 还是邻接表,虚拟出一条边
    }   
//  for(int u=1;u<=n;u++)
//  {
//      for(int j=hk[u];j;j=edge[j].nxt)
//      {
//          cout<<u<<","<<edge[j].to<<endl;
//      }
//  }
    init();
    tarjan(s,0);
    for(int i=1;i<=k;i++)//怎么取出两个点的lca ?
    {
        cf[p[i]]++;cf[q[i]]++; 
        int o;
        if(dfn[p[i]]>dfn[q[i]]) 
            o=p[i];
        else o=q[i];
        if(bis[o]) continue;//比如4-5,4是结束时间戳久的那个,然后遍历所有以4为端点的lca 
        bis[o]=1;//然后5-4,4是结束时间戳,然后又遍历了一遍,重复记录lca 
        for(int j=hk[o];j;j=edge[j].nxt)
        {
            cf[edge[j].lca]--,cf[up[edge[j].lca]]--;//(1).这个父亲已经改变了,因为并查集中的路径压缩 
        }
    } 
//  for(int i=1;i<=n;i++)
//      cout<<i<<","<<cf[i]<<endl;
    int mx=0;
    //不知道最后一块怎么写,不是暴力一个一个写么?从叶节点向根节点统计 
//  for(int x=1;x<=n;x++)
//  {
//      if(du[x]==1)//叶子节点 
//      {
//          while(x!=s)
//          {
//              cf[fa[x]]+=cf[x];//因为路径压缩,这里的fa已经不是原来的父节点了 
//              if(cf[fa[x]]>mx) mx=cf[fa[x]]; 
//          }
//      }   
//  }
    dfs(s,0); 
    for(int i=1;i<=n;i++)
        if(mx<nod[i]) mx=nod[i];
    cout<<mx<<endl; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值