hdu 4338 Simple Path

題意:有N個點,M條邊,下面M行給出M條邊後,有Q個詢問,給你兩個點,起點s,終點t,問從起點s到終點t的簡單路徑中(簡單路徑沒有環),不包括圖中的哪些點,如果st重合,那麼就輸出N-1,如果s無法到達t,輸出N,否則輸出路徑中不包括的點的個數。

用點雙連通分量建圖(割點可以在不同的塊中),然後求LCA

比如對於這樣的圖:



拿割點和點雙連通分量建圖後:


紅色的對應的就是割點,黑色的是點連通分量, 當求(1,7)時候,我們分別查看1,7對應所在樹中的集合,求出路徑上所有的和 sum = 2 + 1 + 3 + 1 +3 + 1 + 2 = 13,由於割點被重複計算,計算的次數爲樹邊的數量。

len = 6 ans = sum - len = 7 ,答案就是可能停留的有7個點。

然後就是如何實現如上問題。

如上圖,求1,2的最短距離即爲lca1,2),而結果就是,定義樹根到某點路徑所有的長度爲son[i],nb[i]爲集合中樹節點中元素個數

sum = son[1]+son[2]-2*son[3]+bn[3]

len = deep[1]+deep[2]-2*deep[3]


1.首先是用倍增法求LCA,因爲會棧溢出所以手寫了棧。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <stack>
using namespace std;
const int N=200005;
#define Log 22
struct Edge
{
    int v;
    Edge *nxt;
}memo[N*10],*cur,*h_bef[N],*h_aft[N];
void addEdge(int u,int v,Edge* head[])
{
    cur->v=v; cur->nxt=head[u];
    head[u]=cur++;
}
int bnum,bn[N];			//有多少個點雙連通分量,每個分量有多少個點。
vector<int> block[N];	//保存每個點雙連通分量
bool iscut[N];			//是否是割點

bool vis[N];
stack<int> stk;			//tarjan用到的
int dfn[N],low[N],son_root,idx;

int lab[N];				//重新建圖後每個點的編號
int id[N];				//重新建圖後每個點在哪棵樹內
int son[N];				//從當前點到其子樹根結點有多少個結點
int fa[N];				//存儲手寫棧的過程中子結點是什麼狀態

int dp[N][Log],dep[N];	//倍增法求LCA

void tarjan(int pt_u,int pt_pre)//手寫棧版tarjan
{
    stack<int> stk_tar;
    stk_tar.push(pt_u);    fa[pt_u]=pt_pre;

    while(stk_tar.size())
    {
        int u=stk_tar.top();
        int pre=fa[u];

        if(dfn[u]==0) dfn[u]=low[u]=++idx,stk.push(u);

        Edge* it;
        for(it=h_bef[u];it;it=it->nxt)
        {
            int v=it->v;
            if(v==pre) continue;

            if(fa[v]==-1)
            {
                fa[v]=u;  stk_tar.push(v);
                h_bef[u]=it;
                break;
            }
            else
            {
                if(fa[v]==u)
                {
                    low[u]=min(low[u],low[v]);

                    if(dfn[u]<=low[v])
                    {
                        if(pre==-2) son_root++;
                        else iscut[u]=1;

                        while(1)
                        {
                            int top=stk.top(); stk.pop();
                            block[bnum].push_back(top);
                            if(top==v) break;
                        }
                        block[bnum].push_back(u);
                        bnum++;
                    }
                }

                else low[u]=min(low[u],dfn[v]);
            }
        }
        if(it==NULL) stk_tar.pop();
    }
}
void dfs(int pt_u,int pt_pre,int iid)//手寫棧dfs,將在一個塊內的點染成一個顏色,得到用倍增法求LCA的信息。
{
    stack<int> stk_dfs;
    stk_dfs.push(pt_u);    fa[pt_u]=pt_pre;

    while(stk_dfs.size())
    {
        int u=stk_dfs.top();
        int pre=fa[u];

        if(dep[u]==0)
        {
            dep[u]=dep[ dp[u][0] ]+1;
            for(int i=1;i<Log;i++) dp[u][i]=dp[ dp[u][i-1] ][ i-1 ];

            id[u]=iid;    son[u]=(pre==-1?0:son[pre])+bn[u];
        }

        Edge* it;
        for(it=h_aft[u];it;it=it->nxt)
        {
            int v=it->v;
            if(v==pre) continue;

            dp[v][0]=u; fa[v]=u;
            stk_dfs.push(v);
            h_aft[u]=it->nxt;
            break;
        }
        if(it==NULL) stk_dfs.pop();
    }
}
int lca(int u,int v)//倍增法求LCA
{
    if(dep[u]<dep[v]) swap(u,v);
    for(int st=(1<<(Log-1)),i=Log-1;i>=0;st>>=1,i--)
    {
        if(st<=dep[u]-dep[v])
        {
            u=dp[u][i];
        }
    }

    if(u==v) return u;

    for(int i=Log-1;i>=0;i--)
    {
        if(dp[u][i]!=dp[v][i])
        {
            u=dp[u][i];
            v=dp[v][i];
        }
    }
    return dp[u][0];
}
void init()
{
    cur=memo;
    memset(h_bef,0,sizeof(h_bef));
    memset(h_aft,0,sizeof(h_aft));
    memset(dp,0,sizeof(dp));

    bnum=0; idx=0;    son_root=0;
    while(stk.size()) stk.pop();
    for(int i=0;i<N;i++)
    {
        block[i].clear();
        dfn[i]=0;    low[i]=0;    lab[i]=-1;
        id[i]=i;    iscut[i]=0;    bn[i]=0;
        son[i]=0;    dep[i]=0;      vis[i]=0;
        fa[i]=-1;
    }
}
int main()
{
    int n,m,Q,t_cnt=0;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();

        for(int i=0;i<m;i++)
        {
            int u,v; scanf("%d%d",&u,&v);
            addEdge(u,v,h_bef);
            addEdge(v,u,h_bef);
        }

        for(int i=0;i<n;i++) if(dfn[i]==0)
        {
            son_root=0;
            tarjan(i,-2);
            if(son_root>1) iscut[i]=1;
        }

        int k=0;
        for(int i=0;i<n;i++) if(iscut[i])
        {
            lab[i]=k; bn[k]=1; k++;
        }

        for(int i=0;i<bnum;i++)
        {
            for(int j=0;j<(int)block[i].size();j++)
            {
                int u=block[i][j];
                if(iscut[u])					//利用割點重新建圖
                {
                    addEdge(lab[u],k,h_aft);
                    addEdge(k,lab[u],h_aft);
                }
                else lab[u]=k;
            }
            bn[k]=(int)block[i].size();
            k++;
        }

        memset(fa,-1,sizeof(fa));
        for(int i=0;i<k;i++) if(dep[i]==0) dfs(i,-1,i);

        scanf("%d",&Q);

        printf("Case #%d:\n",++t_cnt);
        while(Q--)
        {
            int s,t; scanf("%d%d",&s,&t);
            if(s==t) printf("%d\n",n-1);
            else if(lab[s]==-1||lab[t]==-1||id[lab[s]]!=id[lab[t]]) printf("%d\n",n);
            else
            {
                s=lab[s]; t=lab[t];
                int parent=lca(s,t);
                int cnt=son[s]+son[t]-2*son[parent]+bn[parent];
                int len=dep[s]+dep[t]-2*dep[parent];
                int ans=cnt-len;
                printf("%d\n",n-ans);
            }
        }
        puts("");
    }
    return 0;
}

2.非手寫棧,倍增法求LCA(就只是在代碼最開始處加了一行)

#pragma comment(linker, "/STACK:102400000,102400000")

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <stack>
using namespace std;
const int N=200005;
#define Log 22
struct Edge
{
    int v;
    Edge *nxt;
}memo[N*10],*cur,*h_bef[N],*h_aft[N];
void addEdge(int u,int v,Edge* head[])
{
    cur->v=v; cur->nxt=head[u];
    head[u]=cur++;
}
int bnum,bn[N];
vector<int> block[N];
bool iscut[N];

bool vis[N];
stack<int> stk;
int dfn[N],low[N],son_root,idx;

int lab[N],id[N],son[N],fa[N];

int dp[N][Log],dep[N];

void tarjan(int pt_u,int pt_pre)
{
    stack<int> stk_tar;
    stk_tar.push(pt_u);    fa[pt_u]=pt_pre;

    while(stk_tar.size())
    {
        int u=stk_tar.top();
        int pre=fa[u];

        if(dfn[u]==0) dfn[u]=low[u]=++idx,stk.push(u);

        Edge* it;
        for(it=h_bef[u];it;it=it->nxt)
        {
            int v=it->v;
            if(v==pre) continue;

            if(fa[v]==-1)
            {
                fa[v]=u;  stk_tar.push(v);
                h_bef[u]=it;
                break;
            }
            else
            {
                if(fa[v]==u)
                {
                    low[u]=min(low[u],low[v]);

                    if(dfn[u]<=low[v])
                    {
                        if(pre==-1) son_root++;
                        else iscut[u]=1;

                        while(1)
                        {
                            int top=stk.top(); stk.pop();
                            block[bnum].push_back(top);
                            if(top==v) break;
                        }
                        block[bnum].push_back(u);
                        bnum++;
                    }
                }

                else low[u]=min(low[u],dfn[v]);
            }
        }
        if(it==NULL) stk_tar.pop();
    }

}
void dfs(int u,int pre,int cnt,int iid)
{
    dep[u]=dep[ dp[u][0] ]+1;
    for(int i=1;i<Log;i++) dp[u][i]= dp[ dp[u][i-1] ][ i-1 ];

    id[u]=iid;    son[u]=cnt+bn[u];
    for(Edge* it=h_aft[u];it;it=it->nxt)
    {
        int v=it->v;
        if(v!=pre)
        {
            dp[v][0]=u;
            dfs(v,u,son[u],iid);
        }
    }
}
int lca(int u,int v)
{
    if(dep[u]<dep[v]) swap(u,v);
    for(int st=(1<<(Log-1)),i=Log-1;i>=0;st>>=1,i--)
    {
        if(st<=dep[u]-dep[v])
        {
            u=dp[u][i];
        }
    }

    if(u==v) return u;

    for(int i=Log-1;i>=0;i--)
    {
        if(dp[u][i]!=dp[v][i])
        {
            u=dp[u][i];
            v=dp[v][i];
        }
    }
    return dp[u][0];
}
void init()
{
    cur=memo;
    memset(h_bef,0,sizeof(h_bef));
    memset(h_aft,0,sizeof(h_aft));
    memset(dp,0,sizeof(dp));

    bnum=0; idx=0;    son_root=0;
    while(stk.size()) stk.pop();
    for(int i=0;i<N;i++)
    {
        block[i].clear();
        dfn[i]=0;    low[i]=0;    lab[i]=-1;
        id[i]=i;    iscut[i]=0;    bn[i]=0;
        son[i]=0;    dep[i]=0;      vis[i]=0;
        fa[i]=-1;
    }
}
int main()
{
    int n,m,Q,t_cnt=0;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();

        for(int i=0;i<m;i++)
        {
            int u,v; scanf("%d%d",&u,&v);
            addEdge(u,v,h_bef);
            addEdge(v,u,h_bef);
        }

        for(int i=0;i<n;i++) if(dfn[i]==0)
        {
            son_root=0;
            tarjan(i,-2);
            if(son_root>1) iscut[i]=1;
        }

        int k=0;
        for(int i=0;i<n;i++) if(iscut[i])
        {
            lab[i]=k; bn[k]=1; k++;
        }

        for(int i=0;i<bnum;i++)
        {
            for(int j=0;j<(int)block[i].size();j++)
            {
                int u=block[i][j];
                if(iscut[u])
                {
                    addEdge(lab[u],k,h_aft);
                    addEdge(k,lab[u],h_aft);
                }
                else lab[u]=k;
            }
            bn[k]=(int)block[i].size();
            k++;
        }

        for(int i=0;i<k;i++) if(dep[i]==0) dfs(i,-1,0,i);

        scanf("%d",&Q);

        printf("Case #%d:\n",++t_cnt);
        while(Q--)
        {
            int s,t; scanf("%d%d",&s,&t);
            if(s==t) printf("%d\n",n-1);
            else if(lab[s]==-1||lab[t]==-1||id[lab[s]]!=id[lab[t]]) printf("%d\n",n);
            else
            {
                s=lab[s]; t=lab[t];
                int parent=lca(s,t);
                int cnt=son[s]+son[t]-2*son[parent]+bn[parent];
                int len=dep[s]+dep[t]-2*dep[parent];
                int ans=cnt-len;
                printf("%d\n",n-ans);
            }
        }
        puts("");
    }
    return 0;
}


3.非手寫棧,求LCA轉化成RMQ。
#pragma comment(linker, "/STACK:102400000,102400000")

#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstring>
using namespace std;

#define N 200005
struct Edge
{
    int v;
    Edge* nxt;
}memo[N*10],*cur,*h_bef[N],*h_aft[N];
void addEdge(int u,int v,Edge* head[])
{
    cur->v=v; cur->nxt=head[u];
    head[u]=cur++;
}

stack<int> stk;
int dfn[N],low[N],iscut[N];
int idx,son_root;

int bnum;
vector <int> block[N];

int lab[N],bn[N];

int dp[N*2][20];
int dpid[N*2][20];
int dep[N*2],pos[N*2],fa[N*2],son[N*2],ihash[N*2];

void tarjan(int u,int pre)
{
    stk.push(u);
    dfn[u]=low[u]=++idx;
    for(Edge* it=h_bef[u];it;it=it->nxt)
    {
        int v=it->v;
        if(v==pre) continue;
        
        if(dfn[v]==-1)
        {
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u])
            {
                if(pre == -1)son_root++;
                else iscut[u]=1;
                   while(1) 
                {
                    int top=stk.top(); stk.pop();
                    block[bnum].push_back(top);
                    if(top==v) break;
                }
                block[bnum].push_back(u);
                bnum++;
            }
        }
        else low[u]=min(low[u],dfn[v]);
    }
}


void Create_ST(int len)
{
    for(int i=0;i<len;i++)
    {
        dp[i][0]=i;
    }
    for(int j=1;(1<<j)<=len; j++ )
        for(int i=0;i+(1<<j)-1<len;i++)
            if(dep[ dp[i][j-1] ]<dep[ dp[i+(1<<(j-1))][j-1] ])
            {
                dp[i][j] = dp[i][j-1] ;
            }
            else
            {
                dp[i][j] = dp[i+(1<<(j-1))][j-1];
            }
}
int lca(int a,int b)
{
    if(pos[a]>pos[b]) swap(a,b);
    a=pos[a]; b=pos[b];
    int k = (int)(log((double)(b*1.0-a+1))/log(2.0));

    int tmp1=a,tmp2=b-(1<<k)+1;
    if(dep[ dp[tmp1][k] ]<dep[ dp[tmp2][k] ]) return ihash[dp[tmp1][k]];
    else return ihash[dp[tmp2][k]];
}
void dfs(int u,int pre,int d,int cnt,int id)
{
    if(pos[u]==-1)
    {
        pos[u]=idx;
        fa[u]=id;
    }
    ihash[idx]=u;
    dep[idx++]=d;
    son[u]=cnt+bn[u];
    for(Edge* it=h_aft[u];it;it=it->nxt)
    {
        int v=it->v;
        if(v==pre)continue;

        dfs(v,u,d+1,son[u],id);
        ihash[idx]=u;
        dep[idx++]=d;
    }
}
void init()
{
    cur=memo;
    memset(h_bef,0,sizeof(h_bef));
    memset(h_aft,0,sizeof(h_aft));
    
    for(int i=0;i<N;i++) block[i].clear();
    while(stk.size()) stk.pop();    

    memset(lab,-1,sizeof(lab));
    memset(iscut,0,sizeof(iscut));
    memset(dfn,-1,sizeof(dfn));
    idx=bnum=0;

    memset(pos,-1,sizeof(pos));
}
int main()
{
    int n,m,Q,t_cnt=0;
    while(~scanf("%d%d",&n,&m))
    {
        init();
        
        for(int i=0;i<m;i++)
        {
            int u,v;    scanf("%d%d",&u,&v);
            addEdge(u,v,h_bef);
            addEdge(v,u,h_bef);
        }

        for(int i=0;i<n;i++) if(dfn[i]==-1)
        {
            son_root=0;
            tarjan(i,-1);
            iscut[i]=(son_root>1);
        }
        
        int k=0;
        for(int i=0;i<n;i++) if(iscut[i])
        {
            lab[i]=k; bn[k]=1; k++;
        }
        for(int i=0;i<bnum;i++)
        {
            for(int j=0;j<(int)block[i].size();j++)
            {
                int u=block[i][j];
                if(iscut[u])
                {
                    addEdge(lab[u],k,h_aft);
                    addEdge(k,lab[u],h_aft);
                }
                else
                {
                    lab[u]=k;
                }
            }
            bn[k]=int(block[i].size()); k++;
        }
        
        idx=0;
        for(int i=0;i<k;i++) if(pos[i]==-1) dfs(i,-1,0,0,i);
        
        Create_ST(idx);

        printf("Case #%d:\n",++t_cnt);
        scanf("%d",&Q);
        while(Q--)
        {
            int s,t;    scanf("%d%d",&s,&t);
            if(s==t) printf("%d\n",n-1);
            else if(lab[s]==-1 || lab[t]==-1 || fa[lab[s]] != fa[lab[t]]) printf("%d\n",n);
			else 
			{
				int u = lab[s]; 
				int v = lab[t];
				int father=lca(u,v); 
				int ans=son[u]+son[v]-2*son[father]+bn[father];
				int len=dep[pos[u]]+dep[pos[v]]-2*dep[pos[father]];
				ans -= len;
				printf("%d\n",n-ans);
			}
        }
        puts("");
    }
    return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章