D151 拓扑排序 P1983 [NOIP 2013 普及组] 车站分级

D151 拓扑排序 P1983 [NOIP 2013 普及组] 车站分级_哔哩哔哩_bilibili

 

P1983 [NOIP 2013 普及组] 车站分级 - 洛谷

不停站的点的级别低,停站的点级别高,满足 m 趟车次的运行,问 n 个车站至少分为几个不同的级别。

思路

很容易想到从级别低的点向级别高的点连有向边,跑拓扑排序,计算最长路

最坏情况有 1000 趟车次,每趟有 1000 个站点,每趟有 500 个点停站,有 500 个点不停站

不停站的点都向停站的点连有向边,则有 $1000*500∗500=2.5\times 10^8$ 条边,超时

每趟车次建一个中间虚点,不停站的点向虚点连边权为 0 的边,虚点向停站的点连边权为 1 的边

最多有 $1000*(500+500)=10^6$ 条边,不超时

n 个点,m 趟车次,每趟车次建一个虚点,最多有 $n + m = 2000$ 个点

点分两类,这样的二分图,考虑建虚点,边的条数从 $N^2$ 降为 $2N$

相关板子:

D01【模板】拓扑排序 - 董晓 - 博客园

 

// 拓扑排序 O(n+m)
#include<bits/stdc++.h>
using namespace std;

const int N=2010;
vector<pair<int,int>> e[N];
int n,m,a[N],vis[N];
int rd[N],d[N],ans=1;

void topo(){
  for(int i=1; i<=n; i++) d[i]=1; //最低级别为1
  queue<int> q;
  for(int i=1; i<=n+m; i++) if(!rd[i]) q.push(i);
  while(!q.empty()){
    int u=q.front(); q.pop();
    for(auto [v,w]:e[u]){
      d[v]=max(d[v],d[u]+w); //更新最长路
      ans=max(ans,d[v]);
      if(--rd[v]==0) q.push(v);
    }
  }
}
int main(){
  cin>>n>>m; //n个站点 m趟车次
  for(int i=1,s; i<=m; i++){
    memset(vis,0,sizeof vis);
    cin>>s; //停站个数
    for(int j=1; j<=s; j++){
      cin>>a[j];
      vis[a[j]]=1; //停站标记
    }
    int v=n+i; //建中间虚点
    for(int j=a[1]; j<=a[s]; j++){
      if(!vis[j]) e[j].emplace_back(v,0), rd[v]++; //不停站向虚点连边
      else e[v].emplace_back(j,1), rd[j]++; //虚点向停站连边
    }
  }
  topo();
  cout<<ans;
}

 

posted @ 2026-05-02 18:41  董晓  阅读(107)  评论(0)    收藏  举报