1.题意及要点分析
a) 求出从顶级消费者到生产者所有连线数目的和再取模;
b) 需要进行取模的题在每次的中间结果需要取模,防止太大。如本题从某一个顶级消费者为起点到生产者为终点所有连线的和求,其中的每一步都需要取模。在最后计算以所有顶级消费者为起点的连线和时,需要再取一次模。
2.代码示例
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.StringTokenizer;
public class Main {
static final int MOD = 80112002;
static ArrayList<ArrayList<Integer>> eat;
// 二维可变数组,存储该序号代表生物的食物(被吃者)
static int[] dp;
// 存储以该生物为起点至生产者的食物链数目
static boolean[] consumed;
// 标记该序号代表生物是否被吃
// true表示可以被吃,false表示不可被吃,即顶级消费者
static int ans;
static int f(int now) {
if (eat.get(now).isEmpty()) return dp[now] = 1;
// 如果数组eat某序号对应的一组元素为空说明它谁也吃不了,即为生产者,
// 返回1进行计数,表示找到了一条食物链
if (dp[now] != 0) return dp[now];
// 如果某一dp不为零,说明其已被计算过,
// 直接输出,不必再次计算(记忆化,减少时间消耗)
int res = 0;
for (int nxt : eat.get(now)) {
res += f(nxt);
// f(x)代表以序号x为起点到生产者的所有食物链的数目.
//假设x的食物为a,b,c,... 则f(x)=f(a)+f(b)+f(c)+...
res %= MOD; // 取模,防止溢出,这里是符合提议要求的
}
return dp[now] = res;
}
public static void main(String[] args) throws IOException {
// 数据输入
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
int m = Integer.parseInt(st.nextToken());
eat = new ArrayList<>();
dp = new int[n + 1];
consumed = new boolean[n + 1];
for (int i = 0; i <= n; i++) {
eat.add(new ArrayList<>());
}
for (int i = 0; i < m; i++) {
st = new StringTokenizer(br.readLine());
int x = Integer.parseInt(st.nextToken());
int y = Integer.parseInt(st.nextToken());
eat.get(y).add(x);
consumed[x] = true;
}
// 寻找顶级消费者,计算所有从顶级消费者为起点到生产者的食物链之和
for (int i = 1; i <= n; i++) {
if (!consumed[i]) {
ans = (ans + f(i))%MOD; // 取模,防止溢出,符合题意
}
}
System.out.println(ans);
br.close();
}
}