F Fief
这个题是比赛的时候放弃的题...
给定一个无向图,每次询问两个点a,b,问是否存在一个排列,起点是a,终点是b。且每个前缀后缀都联通。
考虑一个简单环上的一个点出发,到达任意一个点都是满足题意的。从环这个角度出发去想的话,我们想到的就是点双和边双,可边双是把图分成若干个部分,而我们想要的是这个图仍然联通来着,那么从点双出发,我们vcc缩点,之后图就变成一颗树,每个节点要么代表一个vcc,要么代表一个割点。我们发现如果存在分叉口的话,我们发现后缀是不连通的。所以说明建完图后,只能是个链。否则,就不行。而且只有首尾的两个vcc里的点才行。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=4e5+10;
int n,m,dfn[N],low[N],cnt,num,Stack[N],top,root;
int new_id[N],du[N],vis[N];
vector<int>son[N];
vector<int>vcc[N];
bool cut[N];
inline void tarjan(int x)
{
dfn[x]=low[x]=++num;
Stack[++top]=x;
if(x==root&&son[x].size()==0)
{
vcc[++cnt].push_back(x);
return;
}
int flag=0;
for(auto y:son[x])
{
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x])
{
flag++;
if(x!=root||flag>1) cut[x]=true;
cnt++;
int z;
do
{
z=Stack[top--];
vcc[cnt].push_back(z);
}while(z!=y);
vcc[cnt].push_back(x);
}
}
else low[x]=min(low[x],dfn[y]);
}
}
inline bool check()
{
int cnt=0;
for(int i=1;i<=num;++i) if(du[i]==1) cnt++;
if(cnt==2) return true;
return false;
}
int main()
{
// freopen("1.in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i)
{
int x,y;
scanf("%d%d",&x,&y);
son[x].push_back(y);
son[y].push_back(x);
}
int pg=0;
for(int i=1;i<=n;++i) if(!dfn[i]) root=i,tarjan(i),pg++;
num=cnt;
for(int i=1;i<=n;++i)
if(cut[i]) new_id[i]=++num;
for(int i=1;i<=cnt;++i)
{
for(auto x:vcc[i])
{
if(cut[x])
{
du[i]++;
du[new_id[x]]++;
}
}
}
bool ischain=check();
if(ischain)
{
int ps=0;
for(int i=1;i<=cnt;++i)
{
if(du[i]==1)
{
ps++;
for(auto x:vcc[i]) if(!cut[x]) vis[x]=ps;
}
}
}
scanf("%d",&m);
if(cnt==1)
{
for(int i=1;i<=m;++i) puts("YES");
return 0;
}
if(pg!=1)
{
for(int i=1;i<=m;++i) puts("NO");
return 0;
}
for(int i=1;i<=m;++i)
{
int a,b;
scanf("%d%d",&a,&b);
if(ischain&&vis[a]!=0&&vis[b]!=0&&vis[a]!=vis[b]) puts("YES");
else puts("NO");
}
return 0;
}