题面
分析
一道比较容易的拓扑排序题目
(当然也可以使用\(vector\)和一些奇奇怪怪的做法暴力碾过)
我们把每一个朋友组视为一个一类点,然后把每头牛也视为二类点
然后把每头牛代表的二类点向所有包含这头牛的朋友组代表的一类点连双向边
接下来我们只需要暴力即可,每次找到当前入度为\(1\)的一类点,然后把其放入队即可(注意\(vis\)数组标记这个一类点有没有入队过)
然后暴力更新就行了,拓扑排序板子
代码
#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
x=0;char ch=getchar();bool f=false;
while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=f?-x:x;
return ;
}
template <typename T>
inline void write(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10^48);
return ;
}
#define ll long long
const int N=1e6+5;
int n,m,inv[N],v,len,len1,ans;
bool vis[N];
vector<int> g[N],G[N];
void topu(){
queue<int> que,p;
que.push(1);vis[1]=true;
while(!que.empty()){
while(!que.empty()){
int u=que.front();
que.pop();len=G[u].size();
for(int i=0;i<len;i++){
v=G[u][i];
inv[v]--;
if(inv[v]==1){
len1=g[v].size();
for(int j=0;j<len1;j++){
if(!vis[g[v][j]]){
vis[g[v][j]]=true;
p.push(g[v][j]);
ans++;
}
}
}
}
}
que=p;
while(!p.empty()) p.pop();
}
return ;
}
int main(){
read(n),read(m);
for(int i=1;i<=m;i++){
read(inv[i]);
for(int j=1;j<=inv[i];j++){
read(v);
G[v].push_back(i);
g[i].push_back(v);
}
}
topu();
write(ans+1);
return 0;
}