题意:有N个点,M条边,下面M行给出M条边后,有Q个询问,给你两个点,起点s,终点t,问从起点s到终点t的简单路径中(简单路径没有环),不包括图中的哪些点,如果s和t重合,那么就输出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的最短距离即为lca(1,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;
}
#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;
}