#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 2010, M = 1000 * 1000 + 10;
int n, m;
int d[N];//记录一个点的入度
bool st[N];//标记该点是否为停靠点
int dist[N];//dist[i]表示i这个点级别最低是多少
int q[N], hh, tt = -1;//拓扑排序数组
int h[N], e[M], ne[M], w[M],idx;//邻接表数组
void add(int a, int b, int c)//邻接表模板
{
d[b] ++ ;//b入度加1
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx;
idx ++ ;
}
void topsort()//拓扑排序模板
{
for (int i = 1; i <= n + m; i ++ )//要算上一个虚拟点
if (!d[i]) q[++ tt] = i;
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if (-- d[j] == 0) q[++ tt] = j;
}
}
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof h);
for (int i = 1; i <= m; i ++ )
{
int cnt;
scanf("%d", &cnt);
memset(st, 0, sizeof st);
int start = n, last = 1;//表示该车次从哪个车站开始到哪个车站结束
while (cnt -- )
{
int stop;
scanf("%d", &stop);
st[stop] = true;//表示stop为停靠点
start = min(start, stop);
last = max(last, stop);
}
int ver = n + i;//在非停靠边和停靠边之间建立一个虚拟点
for (int j = start; j <= last; j ++ )//从start开始到last结束
if (st[j]) add(ver, j, 1);
else add(j, ver, 0);
}
topsort();
for (int i = 1; i <= n; i ++ ) dist[i] = 1;//每个车站级别最低为1,这里也可以建立一个虚拟源点,虚拟源点到每个点的权值为1
for (int i = 0; i < n + m; i ++ )//加上虚拟源点
{
int j = q[i];
for (int k = h[j]; k != -1; k = ne[k])
dist[e[k]] = max(dist[e[k]], dist[j] + w[k]);//求最小的级别要用最长路,这里和差分约束的思想一样
}
int res = 0;
for (int i = 1; i <= n; i ++ ) res = max(res, dist[i]);//找出所有符合条件的级别中最大的那个
cout << res << endl;
return 0;
}
[NOIP2013 普及组] 车站分级
最新推荐文章于 2024-03-22 21:15:59 发布