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; 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章