题目大意:给出一张 n 个点 m 条边的无向连通图,现在有某些点被标记了,问能否通过删除某个未被标记的点,使得删除该点后的数个互不相交的连通块中,至少存在一个联通块中不含有被标记的点
题目分析:首先不难看出,被删掉的点一定是割点,所以我们可以直接用 tarjan 求出割点,然后点双缩一下点,将整个图缩成一棵树,在树上dp即可,dp 的话对于点 u 而言,只需要检查一下其所有子树,以及其父节点上面的那棵子树中,是否存在着不含有标记点的子树即可
代码:
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e6+100;
const int M=1e6+100;
struct Egde
{
int to,next;
}edge1[M],edge2[M];
int head1[N],head2[N],low[N],dfn[N],c[N],Stack[N],new_id[N],dp[N],num,cnt,cnt1,cnt2,tot,root,top,n,m,b;
bool cut[N],ban[N];
vector<int>dcc[N];
vector<int>ans;
map<int,int>mp;
void addedge1(int u,int v)
{
edge1[cnt1].to=v;
edge1[cnt1].next=head1[u];
head1[u]=cnt1++;
}
void addedge2(int u,int v)
{
edge2[cnt2].to=v;
edge2[cnt2].next=head2[u];
head2[u]=cnt2++;
}
void tarjan(int u)
{
dfn[u]=low[u]=++num;
Stack[++top]=u;
if(u==root&&head1[u]==-1)
{
dcc[++cnt].push_back(u);
return;
}
int flag=0;
for(int i=head1[u];i!=-1;i=edge1[i].next)
{
int v=edge1[i].to;
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u])
{
flag++;
if(u!=root||flag>1)
cut[u]=true;
cnt++;
int x;
do
{
x=Stack[top--];
dcc[cnt].push_back(x);
}while(x!=v);
dcc[cnt].push_back(u);
}
}
else
low[u]=min(low[u],dfn[v]);
}
}
void solve()
{
for(int i=1;i<=n;i++)//找割点+缩点
if(!dfn[i])
{
root=i;
tarjan(i);
}
}
void build()//缩点+连边
{
solve();
num=cnt;
for(int i=1;i<=n;i++)
if(cut[i])
{
new_id[i]=++num;
mp[num]=i;
}
for(int i=1;i<=cnt;i++)
for(int j=0;j<dcc[i].size();j++)
{
int x=dcc[i][j];
if(cut[x])
{
addedge2(i,new_id[x]);
addedge2(new_id[x],i);
}
else
c[x]=i;
}
}
void dfs(int u,int fa)
{
bool flag=false;
for(int i=head2[u];i!=-1;i=edge2[i].next)
{
int v=edge2[i].to;
if(v==fa)
continue;
dfs(v,u);
if(!dp[v])
flag=true;
dp[u]+=dp[v];
}
if(mp.count(u)&&!ban[mp[u]]&&(flag||dp[u]==b))
ans.push_back(mp[u]);
}
void init()
{
for(int i=0;i<N;i++)
dcc[i].clear();
cnt=cnt2=cnt1=num=tot=top=0;
memset(head2,-1,sizeof(head2));
memset(head1,-1,sizeof(head1));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(cut,false,sizeof(cut));
memset(c,0,sizeof(c));
memset(Stack,0,sizeof(Stack));
memset(new_id,0,sizeof(new_id));
memset(dp,0,sizeof(dp));
memset(ban,false,sizeof(ban));
mp.clear();
ans.clear();
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
init();
scanf("%d%d%d",&n,&m,&b);
while(m--)
{
int u,v;
scanf("%d%d",&u,&v);
addedge1(u,v);
addedge1(v,u);
}
build();
for(int i=1;i<=b;i++)
{
int x;
scanf("%d",&x);
ban[x]=true;
if(cut[x])
dp[new_id[x]]++;
else
dp[c[x]]++;
}
dfs(1,-1);
sort(ans.begin(),ans.end());
printf("%d\n",ans.size());
for(int num:ans)
printf("%d ",num);
return 0;
}