You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

173 lines
4.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package class_2023_01_2_week;
// 一条单向的铁路线上火车站编号为1~n
// 每个火车站都有一个级别,最低为 1 级。
// 现有若干趟车次在这条线路上行驶,
// 每一趟都满足如下要求:
// 如果这趟车次停靠了火车站 x则始发站、终点站之间所有级别大于等于火车站x的都必须停靠。
//(注意:起始站和终点站自然也算作事先已知需要停靠的站点)
// 现有 m 趟车次的运行情况(全部满足要求),
// 试推算这n个火车站至少分为几个不同的级别。
// 测试链接 : https://www.luogu.com.cn/problem/P1983
// 线段树建边
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下所有代码把主类名改成Main可以直接通过
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.Arrays;
public class Code05_BusStationsMinLevelNumbers {
public static final int maxn = 100001;
// 1 500 600 1000
// stops[1,500,600,1000]
// 停靠车站
public static int[] stops = new int[maxn];
// 一段线段树范围的id编号
// id[rt] = xrt背后的范围这一段给它的点编号是x
// rt -> 线段树的某个范围的固有属性l~r,rt
public static int[] id = new int[maxn << 2];
// id点是否为单点
// a 单点 范围 虚拟点?
// 70~90 rt = 60 -> 17 single[17] ?
public static boolean[] single = new boolean[maxn << 3];
// id点的入度
public static int[] inDegree = new int[maxn << 3];
// id点拓扑排序统计的最大深度(只算路径上的单点数量)
public static int[] singleDeep = new int[maxn << 3];
// 链式前向星建图用
public static int[] head = new int[maxn << 3];
public static int[] to = new int[maxn << 3];
public static int[] next = new int[maxn << 3];
// 拓扑排序用
public static int[] queue = new int[maxn << 3];
// n为车站个数、nth为线段树上范围的编号计数、eth为边的计数
public static int n, nth, eth;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer in = new StreamTokenizer(br);
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
while (in.nextToken() != StreamTokenizer.TT_EOF) {
n = (int) in.nval;
in.nextToken();
int m = (int) in.nval;
nth = 0;
eth = 0;
Arrays.fill(single, 0, (n << 2) + m + 1, false);
Arrays.fill(inDegree, 0, (n << 2) + m + 1, 0);
Arrays.fill(singleDeep, 0, (n << 2) + m + 1, 0);
build(1, n, 1);
for (int i = 0; i < m; i++) {
in.nextToken();
int k = (int) in.nval;
for (int j = 0; j < k; j++) {
in.nextToken();
stops[j] = (int) in.nval;
}
int curVirtual = ++nth;
// 虚点向停靠车站连边
for (int j = 0; j < k; j++) {
vLinkStop(curVirtual, stops[j], 1, n, 1);
}
// 不停靠的连续车站向虚点连边
for (int j = 1; j < k; j++) {
if (stops[j] > stops[j - 1] + 1) {
rangeLinkV(stops[j - 1] + 1, stops[j] - 1, curVirtual, 1, n, 1);
}
}
}
out.println(topoSort());
out.flush();
}
}
public static void build(int l, int r, int rt) {
id[rt] = ++nth;
if (l == r) {
single[id[rt]] = true;
} else {
int m = (l + r) / 2;
build(l, m, rt << 1);
build(m + 1, r, rt << 1 | 1);
addEdge(id[rt << 1], id[rt]);
addEdge(id[rt << 1 | 1], id[rt]);
}
}
public static void rangeLinkV(int L, int R, int vid, int l, int r, int rt) {
if (L <= l && r <= R) {
addEdge(id[rt], vid);
} else {
int m = (l + r) / 2;
if (L <= m) {
rangeLinkV(L, R, vid, l, m, rt << 1);
}
if (R > m) {
rangeLinkV(L, R, vid, m + 1, r, rt << 1 | 1);
}
}
}
// 17 17~17
public static void vLinkStop(int vid, int stop, int l, int r, int rt) {
if (l == r) {
addEdge(vid, id[rt]);
} else {
int m = (l + r) / 2;
// 1~100
// 想去的车站是70 70~70
// 1~50 51~100
if (stop <= m) {
vLinkStop(vid, stop, l, m, rt << 1);
} else {
vLinkStop(vid, stop, m + 1, r, rt << 1 | 1);
}
}
}
public static void addEdge(int fid, int tid) {
inDegree[tid]++;
to[++eth] = tid;
next[eth] = head[fid];
head[fid] = eth;
}
public static int topoSort() {
int l = 0;
int r = 0;
for (int i = 1; i <= nth; i++) {
if (inDegree[i] == 0) {
queue[r++] = i;
if (single[i]) {
singleDeep[i] = 1;
}
}
}
int ans = 0;
while (l < r) {
int curNode = queue[l++];
ans = Math.max(ans, singleDeep[curNode]);
for (int edgeIndex = head[curNode]; edgeIndex != 0; edgeIndex = next[edgeIndex]) {
int child = to[edgeIndex];
singleDeep[child] = Math.max(singleDeep[child], singleDeep[curNode] + (single[child] ? 1 : 0));
if (--inDegree[child] == 0) {
queue[r++] = child;
}
}
}
return ans;
}
}