"蔚来杯"2022牛客暑期多校训练营3 F
"蔚来杯"2022牛客暑期多校训练营3 F
题目大意
给定一个无向图,每次询问两点x,y,求是否存在一个n的排列,使得第一个元素为x,最后一个元素为y,且排列的任意一个前缀,任意一个后缀都联通。
分析
简单分析一下就可以知道,我们对原图缩点后,看缩完点后的图是或否是一条链。
在是一条链的前提下,我们询问的两个点两种情况合法。
- 缩完点之后只有一个联通块,即无割点。则一定合法。
- 缩完点后是一条大于等于两个联通块的链,则询问两点必须在链的两端,则我们只要看两点所待的v-dcc是不是一个点,若是则无解,不是的话,要看其度数是不是1,则可以判断出是否合法。
总的来说,模板题。
AC_code
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10,M = 4e5 + 10;
//要缩点建图的点与边都多开一倍保险
int h[N],e[M],ne[M],idx;
int dfn[N],low[N],ts;
int id[N],d[N];
int dcc_cnt;
bool cut[N];
stack<int> s;
vector<int> dcc[N];
int root,n,m;
void add(int a,int b)
{
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
void tarjan(int u)
{
dfn[u] = low[u] = ++ts;
s.push(u);
if(u==root&&h[u]==-1)
{
dcc_cnt++;
dcc[dcc_cnt].push_back(u);
return ;
}
int cnt = 0;
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(!dfn[j])
{
tarjan(j);
low[u] = min(low[u],low[j]);
if(dfn[u]<=low[j])
{
cnt++;
if(u!=root||cnt>1) cut[u] = 1;
++dcc_cnt;
int y;
do
{
y = s.top();
s.pop();
dcc[dcc_cnt].push_back(y);
}while(y!=j);
dcc[dcc_cnt].push_back(u);
}
}else low[u] = min(low[u],dfn[j]);
}
}
bool get()
{
int num = dcc_cnt;
for(int i=1;i<=n;i++)
if(cut[i])
id[i] = ++num;//给割点一个编号
for(int i=1;i<=dcc_cnt;i++)
for(auto x:dcc[i])
{
if(cut[x]) d[i]++,d[id[x]]++;
else id[x] = i;
}
for(int i=1;i<=num;i++)
if(d[i]>2)
return 0;
return 1;
}
int main()
{
memset(h,-1,sizeof h);
scanf("%d%d",&n,&m);
while(m--)
{
int u,v;scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
bool f = 1,ok = 0;
for(int i=1;i<=n;i++)
if(!dfn[i])
{
root = i;
if(f)
{
tarjan(i);
f = 0;
}
else
{
ok = 1;
break;
}
}
int q;scanf("%d",&q);
if(n==2)
{
while(q--) cout<<"YES\n";
return 0;
}
if(!ok&&!get()) ok = 1;
while(q--)
{
int u,v;scanf("%d%d",&u,&v);
u = id[u],v = id[v];
if(ok) cout<<"NO\n";
else if(!d[u]&&!d[v]) cout<<"YES\n";
else if(d[u]==1&&d[v]==1&&u!=v) cout<<"YES\n";
else cout<<"NO\n";
}
return 0;
}

浙公网安备 33010602011771号