链接:https://www.nowcoder.com/acm/contest/76/E
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
在战争时期,A国派出了许多间谍到其他国家去收集情报。因为间谍需要隐秘自己的身份,所以他们之间只是单向联系。所以,某个间谍只能单向联系到一部分的间谍。同时,间谍也不知道跟他联系的是谁。
HA是间谍们的老大,但他也只能联系到部分的间谍。HA现在有一项命令有告诉所有的间谍。HA想要知道他至少要告诉多少个他能联系上的间谍才能通知到所有的间谍。
输入描述:
有多个测试数据。
对于每个测试数据:
第一行为一个整数n,m(0
第二行为m个用空格隔开的整数xi,代表HA能通知到的间谍的编号;
第三行到第n+2行,每一行第一个整数ai(0<=ai
输出描述:
输出一行,此行中有一个整数,代表HA至少需要联系的间谍数。如果HA不能通知到所有间谍,输出-1。
示例1
输入
3 2
1 2
1 2
1 1
0
输出
-1
示例2
输入
3 1
1
2 2 3
0
0
输出
1
分析: 赛时没写出来,想到的方法是tarjan,但是用的不够熟练,所以没出。
赛后,各种神仙代码。。 数据太弱了,所以 并查集也能过
比较容易想到的是 如果是DAG,只需要通知到那个入度为0的结点的间谍就可以了,但如果存在环,也只需要通知到其中的一个就可以,但是并查集就不能解决。。 数据弱了,样例没过都能A OoO
所以用 tarjan缩点 找入度为0的点并判断是否能通知到该间谍
【tarjan 缩点】
AC代码:
#include
using namespace std;
#define mem(a,n) memset(a,n,sizeof(a))
#define memc(a,b) memcpy(a,b,sizeof(b))
#define rep(i,a,n) for(int i=a;i
#define dec(i,n,a) for(int i=n;i>=a;i--)///[n,a]
#define pb push_back
#define fi first
#define se second
#define IO ios::sync_with_stdio(false)
#define fre freopen("in.txt","r",stdin)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
typedef unsigned long long ull;
const double PI=acos(-1.0);
const double E=2.718281828459045;
const double eps=1e-3;
const int INF=0x3f3f3f3f;
const int MOD=258280327;
const int N=5e2+5;
const ll maxn=1e6+5;
const int dir[4][2]= {-1,0,1,0,0,-1,0,1};
int color[N],vis[N],sta[N];
int ans[N],dfn[N],low[N];
int n,m,scc,cnt,tail;
int in[N];///入度
vector<int>g[N];
void init()
{
cnt=1;
scc=0;///强连通分量个数
tail=0;
mem(in,0);
mem(low,0);
mem(dfn,0);
mem(vis,0);
rep(i,0,n+1) g[i].clear();
}
void tarjan(int u)
{
vis[u]=1; ///访问过标记为1
low[u]=dfn[u]=cnt++;
sta[++tail]=u;///入栈
for(auto v:g[u])
{
if(vis[v]==0)///结点v没有访问过
tarjan(v);
if(vis[v]==1)///结点v访问过
low[u]=min(low[u],low[v]);
}
if(dfn[u]==low[u]) ///一个强联通分量的判断
{
scc++;
do///缩点
{
low[sta[tail]]=scc;///更新low数组的值
color[sta[tail]]=scc;///缩点染色
vis[sta[tail]]=-1;///染色标记
}
while(sta[tail--]!=u);///一直出栈,直到回到最初的点
}
}
int a[N];
void solve()
{
rep(i,1,n+1)
if(!vis[i])
tarjan(i);
rep(i,1,n+1)
{
for(auto v:g[i])
{
if(color[i]!=color[v])
in[color[v]]++;
}
}
int res=0;
bool flag=0;
rep(i,1,scc+1)///遍历所有的连通分量
{
if(!in[i])///结点i入度为0
{
flag=0;///能否通知到间谍i的标记
rep(j,1,n+1)
{
if(color[j]==i&&binary_search(a,a+m,j))
{
res++;
flag=1;
break;
}
}
if(!flag)///不能通知到间谍i
break;
}
}
printf("%d\n",flag?res:-1);
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
init();
rep(i,0,m) scanf("%d",&a[i]);
sort(a,a+m);
rep(i,1,n+1)
{
int x,y;
scanf("%d",&x);
while(x--)
{
scanf("%d",&y);
g[i].pb(y);
}
}
solve();
}
return 0;
}
蒻苣,以下代码 可略过~
错误代码:
【并查集】
#include
using namespace std;
#define mem(a,n) memset(a,n,sizeof(a))
#define memc(a,b) memcpy(a,b,sizeof(b))
#define rep(i,a,n) for(int i=a;i///[a,n)
#define dec(i,n,a) for(int i=n;i>=a;i--)///[n,a]
#define pb push_back
#define fi first
#define se second
#define IO ios::sync_with_stdio(false)
#define fre freopen("in.txt","r",stdin)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
typedef unsigned long long ull;
const double PI=acos(-1.0);
const double E=2.718281828459045;
const double eps=1e-3;
const int INF=0x3f3f3f3f;
const int MOD=258280327;
const int N=5e2+5;
const ll maxn=1e6+5;
const int dir[4][2]= {-1,0,1,0,0,-1,0,1};
int par[N],rrank[N];
int a[N];
void init(int n)
{
rep(i,0,n+1) par[i]=i;
}
int Find(int x)///找根
{
return par[x]==x?x:Find(par[x]);
}
void unite(int a,int b)
{
int c,d;
c=Find(a);
d=Find(b);
if(c==d) return ;
if(rrank[c]else
{
par[d]=c;
if(rrank[c]==rrank[d])
rrank[c]++;
}
}
int main()
{
// fre;
int n,m;
while(cin>>n>>m&&(n+m))
{
mem(rrank,0);
mem(a,0);
init(n);
rep(i,1,m+1) cin>>a[i];
rep(i,1,n+1)
{
int x,y;
cin>>x;
rep(j,0,x)
{
cin>>y;
unite(y,i);
}
}
set<int>st;
rep(i,1,m+1)
{
if(a[i]==par[a[i]])
st.insert(a[i]);
}
bool flag=1;
rep(i,1,n+1)
{
if(i==par[i]&&st.find(i)==st.end())
{
flag=0;
break;
}
}
if(flag) cout<else cout<<-1<return 0;
}