【题目大意】
给你一个无向图。问,从点u到点v,若是只走简单路径,有多少个点不能到达?
【思路】
这题肯定是要双连通缩点的,以前老是觉得点双连通不会用来缩点,因为割点可以属于多个连通集合,现在立马打脸了 o(╯□╰)o
最开始用,边-双连通思考,然后发现下面这种图就....
如果我们询问(6,7)和(6,4),显然两次点6所属集团的意义不同;除此之外,比方(1,2,3)这个集团,直接合并也是不合理的,询问(1,3)和(3,7),点3的意义也是不同的
问题的关键,其实关键就是割点。如果对点双连通比较熟悉的话,应该能想到:除了孤点,任何点都会至少属于一个连通集合,而只有割点能属于2个及以上的集合。连通集合一定是通过割点相连的。
所以,我们可以考虑把原图改成通过割点相连的形式。上面那个图就可以改成
显然,每条边的一段都是连通集合,而另一段是割点。
仔细分析割点到割点、非割点到非割点、割点到非割点三种情况,你会发现,点u到点v,能走的点就是修改后的图(这个图不会有环)对应的简单路径上的所有的点。比方(2,5)能经过的点就为 {2}∪{1,2,3}∪{3}∪{3,5}∪{5} == {1,2,3,5} ,(2,6)的为 {2}∪{1,2,3}∪{3}∪{3,5}∪{5}∪{4,5,6} == {1,2,3,4,5,6}。
因为路径上的点是有重复的,我们仍需要思考一下。实际上,点的数目,等于每个点的点的数目减去路径上的边数。
而具体怎么去求无环图上一个简单路径上的点数和以及边数和。这里可以采用多种方法,我这里用的lca维护一下。
P.S. 数据好像不严,询问中有点的编号>=n的情况,我这种情况是直接输出n
#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<cctype>
#include<string>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<map>
#include<set>
using namespace std;
#define MP(x,y) make_pair((x),(y))
#define PB(x) push_back(x)
typedef __int64 LL;
//typedef unsigned __int64 ULL;
/* ****************** */
const LL INF = 1LL<<55;
const double INFF = 1e100;
const double eps = 1e-8;
const LL mod = 10000000007LL;
const int NN = 100010;
const int MM = 400010;
/* ****************** */
int dfn[NN], low[NN], tsp;
int sta[MM], sta_top;
int id[NN], num[NN*2], id_cnt;
int color[NN];
vector<int>bccno[NN];
struct G
{
int u, v, next;
}E[MM], E1[MM];
int p1[NN], T1;
int p[NN*2], T;
int long2[NN*2*2];
int deep[NN*2], pos[NN*2], dian[NN*2];
int oula[NN*2*2];
int rmq[NN*2*2][20];
void init_long2(int n)
{
int i;
long2[1]=0;
for(i=2;i<=n;i++)
{
long2[i]=long2[i-1]+(i==(i&(-i)));
}
}
void add(int u,int v,G *E,int *p,int &T)
{
E[T].u = u;
E[T].v = v;
E[T].next = p[u];
p[u] = T++;
}
void bcc(int u,int fa,int col)
{
int i, ii, v;
dfn[u] = low[u] = ++tsp;
color[u] = col;
for(i = p1[u]; i + 1; i = E1[i].next)
{
v = E1[i].v;
if(dfn[v]==0)
{
sta[++sta_top] = i;
bcc(v, u, col);
low[u] = min(low[u], low[v]);
if(low[v] >= dfn[u])
{
num[++id_cnt] = 0;
for(;;)
{
ii = sta[sta_top--];
if(id[ E1[ii].u ] != id_cnt)
{
bccno[ E1[ii].u ].PB(id_cnt);
num[id_cnt] ++;
id[ E1[ii].u ] = id_cnt;
}
if(id[ E1[ii].v ] != id_cnt)
{
bccno[ E1[ii].v ].PB(id_cnt);
num[id_cnt] ++;
id[ E1[ii].v ] = id_cnt;
}
if(E1[ii].u == u && E1[ii].v == v)
break;
}
}
}
else if(dfn[v]<dfn[u] && v != fa)
{
sta[++sta_top] = i;
low[u] = min(low[u], dfn[v]);
}
}
}
//生成欧拉序列,计算每个节点深度,每个点首次出现的位置
//其第一个父亲,没有用-1表示
void dfs(int u,int fa,int cen)
{
oula[++tsp]=u;
deep[u]=cen;
pos[u]=tsp;
int i,v;
for(i=p[u];i+1;i=E[i].next)
{
v=E[i].v;
if(v!=fa)
{
dian[v] = dian[u] + num[v];
dfs(v,u,cen+1);
oula[++tsp]=u;
}
}
}
//用于lca的rmq
void init_rmq(int n)
{
int i,j,en,len;
int t1,t2;
for(i=1;i<=n;i++)
rmq[i][0]=i;
for(j=1;j<=long2[n];j++)
{
en=n+1-(1<<j);
len=1<<(j-1);
for(i=1;i<=en;i++)
{
t1=oula[ rmq[i][j-1] ];
t2=oula[ rmq[i+len][j-1] ];
if(deep[t1]<deep[t2])
rmq[i][j]=rmq[i][j-1];
else
rmq[i][j]=rmq[i+len][j-1];
}
}
}
int ask_lca(int u,int v)
{
int st=pos[u];
int en=pos[v];
if(st>en)
swap(st,en);
int k=long2[en-st+1];
int id1=oula[ rmq[st][k] ];
int id2=oula[ rmq[en+1-(1<<k)][k] ];
if(deep[id1]<deep[id2])
return id1;
return id2;
}
void solve(int n,int sum_n)
{
int m,i,u,v,ans;
tsp=0;
for(i=1;i<=n;i++)
pos[i] = -1;
for(i=1;i<=n;i++)
{
if(pos[i]==-1)
{
dian[i] = num[i];
dfs(i,-1,0);
}
}
init_rmq(tsp);
scanf("%d",&m);
while(m--)
{
scanf("%d%d",&u,&v);
if(u>=sum_n || v>=sum_n)
{
printf("%d\n",sum_n);
// while(1);
continue;
}
u++, v++;
if(u==v)
printf("%d\n", sum_n-1);
else if(color[u]!=color[v])
printf("%d\n", sum_n);
else
{
u = id[u];
v = id[v];
i = ask_lca(u, v);
ans = dian[u] + dian[v] - dian[i]*2 + num[i];
ans -= (deep[u]+deep[v]-deep[i]*2);
ans = sum_n - ans;
printf("%d\n",ans);
}
}
puts("");
}
int main()
{
init_long2(100000*4);
int n, m, i, j, u, v, si;
int ee = 0;
while(scanf("%d%d", &n, &m) != EOF)
{
memset(p1, -1, sizeof(p1));
T1 = 0;
for(i = 0; i < m; i ++)
{
scanf("%d%d", &u, &v);
u ++, v ++;
add(u, v, E1, p1, T1);
add(v, u, E1, p1, T1);
}
memset(dfn, 0, sizeof(dfn));
memset(id, -1, sizeof(id));
tsp = 0;
id_cnt = 0;
sta_top = 0;
for(i = 1; i <= n; i ++)
{
if(dfn[i]==0)
{
bcc(i, -1, i);
}
}
memset(p, -1, sizeof(p));
T = 0;
for(i = 1; i <= n; i ++)
{
si = bccno[i].size();
if(si > 1)
{
u = ++id_cnt;
id[i] = u;
num[u] = 1;
for(j = 0; j < si; j ++)
{
v = bccno[i][j];
add(u, v, E, p, T);
add(v, u, E, p, T);
}
}
bccno[i].clear();
}
printf("Case #%d:\n",++ee);
solve(id_cnt, n);
}
return 0;
}