NC15707 可达性
题目:
给出一个 0 ≤ N ≤ 105 点数、0 ≤ M ≤ 105 边数的有向图,
输出一个尽可能小的点集,使得从这些点出发能够到达任意一点,如果有多个这样的集合,
输出这些集合升序排序后字典序最小的。
题解:
• 先强连通缩点,变成一个有向无环图
• 然后在所有入度为0的点集,每一个拿出编号最小的一个点
记录缩点后的入度
for(int i=1;i<=n;i++){for(int j=head[i];j!=-1;j=edge[j].next){int u=color[i];int v=color[edge[j].v];if(u!=v)in[v]++;//记录入度 }}
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
const int INF = 0x3f3f3f3f;
struct node
{int v, next;
}edge[maxn];
vector<int>p;
stack <int> s;
int dfn[maxn],low[maxn];
int color[maxn],in[maxn];
int tot,block;
int head[maxn],pre[maxn];
int n,m,cnt,C,X;
bool vis[maxn];
void add(int from, int to) {edge[++cnt].v=to;edge[cnt].next=head[from];head[from]=cnt;
}
void tarjan(int u) {dfn[u]=low[u]= ++tot;vis[u]=1;s.push(u);for(int i=head[u];i!=-1;i=edge[i].next) {int v=edge[i].v;if (!dfn[v]) {tarjan(v);low[u]= min(low[u],low[v]);}else if(vis[v]) low[u]=min(low[u],dfn[v]);}if (dfn[u]==low[u]) {block++;int v;do {v=s.top();s.pop();color[v]=block;pre[block]=min(v, pre[block]);//pre保存的是同强连通分量中编号最小点 vis[v]=0;} while (v!=u);}
}int main()
{cin>>n>>m;memset(head,-1,sizeof(head));memset(pre,INF,sizeof(pre));for(int i=0;i<m;i++){int u,v;scanf("%d%d",&u,&v);add(u,v);}for(int i=1;i<=n;i++){if(!dfn[i]) tarjan(i);}if(block==1){cout <<"1"<< endl; return 0; }for(int i=1;i<=n;i++){for(int j=head[i];j!=-1;j=edge[j].next){int u=color[i];int v=color[edge[j].v];if(u!=v)in[v]++;//记录入度 }}for(int i=1;i<=block;i++){//此处已经进行完压缩,压缩后还有block个点 if(!in[i]) //如果一个点没有入度 p.push_back(pre[i]);}sort(p.begin(),p.end());cout<<p.size()<<endl;for(int i=0;i<p.size();i++) cout<<p[i]<< " ";cout<<endl;return 0;
}
NC 19960 [HAOI2006]受欢迎的牛
题目:
• 每一头牛的愿望就是变成一头最受欢迎的牛。现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎。
• 这种关系是具有传递性的,如果A认为B受欢迎,B认为C受欢迎,那么牛A也认为牛C受欢迎。
• 你的任务是求出有多少头牛被所有的牛认为是受欢迎的。
题解:
• 强连通缩点
• 唯一的一个出度为0的强连通分量
• 如果有多个出度为0的强连通分量则无解
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
const int INF = 0x3f3f3f3f;
struct node
{int v, next;
}edge[maxn];
vector<int>p;
stack <int> s;
int dfn[maxn],low[maxn];
int color[maxn],in[maxn];
int tot,block;
int head[maxn],pre[maxn];
int n,m,cnt,C,X;
bool vis[maxn];
void add(int from, int to) {edge[++cnt].v=to;edge[cnt].next=head[from];head[from]=cnt;
}
void tarjan(int u) {dfn[u]=low[u]= ++tot;vis[u]=1;s.push(u);for(int i=head[u];i!=-1;i=edge[i].next) {int v=edge[i].v;if (!dfn[v]) {tarjan(v);low[u]= min(low[u],low[v]);}else if(vis[v]) low[u]=min(low[u],dfn[v]);}if (dfn[u]==low[u]) {block++;int v;do {v=s.top();s.pop();color[v]=block;pre[block]=min(v, pre[block]);//pre保存的是同强连通分量中编号最小点 vis[v]=0;} while (v!=u);}
}int main()
{cin>>n>>m;memset(head,-1,sizeof(head));memset(pre,INF,sizeof(pre));for(int i=0;i<m;i++){int u,v;scanf("%d%d",&u,&v);add(u,v);}for(int i=1;i<=n;i++){if(!dfn[i]) tarjan(i);}for(int i=1;i<=n;i++){for(int j=head[i];j!=-1;j=edge[j].next){int u=color[i];int v=color[edge[j].v];if(u!=v)in[u]++; }}for(int i=1;i<=n;i++){//此处已经进行完压缩,压缩后还有block个点 if(!in[color[i]]) //如果一个点没有入度 p.push_back(pre[i]);}//sort(p.begin(),p.end());//cout<<ans;cout<<p.size()<<endl;//for(int i=0;i<p.size();i++) cout<<p[i]<< " ";//cout<<endl;return 0;
}