题意
给你一个有向图,N个点中有1-L个为发送点,剩下的为接收点。点1发送一封邮件给他相邻的点,其他的点接着发送,可能会收到多封邮件,每封发送。问接收点一共接收到了多少封邮件,以及多少个接收点收到了邮件。
思路
建图,跑一遍bfs可以求出能到达几个接收点,同时优化得到一个连通图。每个接受点受到的邮件数目等于所有上一级发送的邮件数目之和。所以跑一遍拓扑排序即可,每次更新该点可以接收邮件数目
ci[edge[i].to] = ci[p] + ci[edge[i].to]
code
#include <iostream>
#include <algorithm>
#include <string>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 2e3 + 10;
const int mod = 1e9+7;
struct Node
{
int to,next;
int flag=0;//是否为连通图
}edge[maxn * maxn];
int head[maxn];
int in[maxn];
int ci[maxn];
int cnt;
ll ans1,ans2;
int vis[maxn];
int n,m;
queue <int> Q;
void addage(int u, int v, int mark = 0) //链式前向星存储图
{
if(!mark) //第一遍求接受点不需要求每个点的入度
{
cnt++;
edge[cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt;
}
if(mark) //优化后的连通图直接求入度
in[v]++;
}
void bfs() //寻找有几个有效的接收点
{
ans2 = 0;
while(!Q.empty()) Q.pop();
memset(vis,0,sizeof(vis));
Q.push(1);
vis[1] = 1;
while(!Q.empty())
{
int p = Q.front();
Q.pop();
if(p>m)
ans2++;
for(int i = head[p]; i; i = edge[i].next)
{
addage(p,edge[i].to,1); //存在通路的标记
if(!vis[edge[i].to])
{
vis[edge[i].to] = 1;
Q.push(edge[i].to);
}
}
}
}
void topu() //算总共受到多少邮件
{
memset(ci,0,sizeof(ci));
while(!Q.empty())
Q.pop();
Q.push(1);
ci[1] = 1;
while(!Q.empty())
{
int p = Q.front();
Q.pop();
for(int i = head[p]; i != 0; i = edge[i].next)
{
ci[edge[i].to] = (ci[p] + ci[edge[i].to]) % mod; //求出每个点接收到的数目
in[edge[i].to]--; //入度减1
if(in[edge[i].to] == 0)
{
Q.push(edge[i].to); //当入度为0时,收到的数目已经确定,就可以入队列搜下一层
}
}
}
ans1 = 0;
for(int i = m + 1; i <= n; i++)
ans1 = (ans1 + ci[i]) % mod;
}
int main()
{
cin>>n>>m;
for(int i = 1;i <= m;i++)
{
int a;
cin>>a;
for(int j = 1;j <= a;j++)
{
int b;
scanf("%d",&b);
addage(i,b);
}
}
bfs();
topu();
cout<<ans1<<' '<<ans2;
return 0;
}