抢救实验数据

题目背景

某大型实验中心的一个实验室发生了毒气泄露,现在实验员想要抢救实验数据。

题目描述

实验中心可以看做一个 nn 个点 mm 条边的无向联通图。
所有实验员每秒可以走到一个相邻的实验室并收集其中的数据,毒气每秒会蔓延到所有的相邻实验室。 当一个实验员回到了大厅 s,我们称他抢救了数据。
实验员不能进入有毒气的实验室(如果他和毒气在同一秒进入实验室也不行)。
大厅周围有严格的保护措施,不会被毒气蔓延。(具体可以参考样例二)
现在所有实验员都在大厅 s,毒气泄露的实验室为点 t。假如有足够多的实验员同时出发,请问最多能抢救多少个实验室的数据?

输入格式

第一行两个正整数 n,m,表示实验中心的点数和边数。
第二至 m+1 行每行两个正整数 u,v,代表 u,v实验室之间有一条边。
第 m+2行两个正整数 s,t,表示大厅和毒气泄露点。

输出格式

一行一个整数,表示最多能抢救多少个的实验室的数据。

输入输出样例

输入 #1

4 3
1 2
2 3
3 4
1 4

输出 #1

1

输入 #2

6 7
1 2
2 3
3 1
4 5
5 6
6 4
1 4
1 4

输出 #2

2

输入 #3

15 14
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
3 11
11 12
12 13
13 14
14 15
1 10

输出 #3

6

说明/提示

请注意常数因子带来的程序效率上的影响。

【样例解释一】
只有 2 号实验室可以到达并回来。

【样例解释二】
因为大厅是坚不可摧的,所以 5,6 两个实验室会被毒气蔓延到,而 2,3 两个实验室永远不会被蔓延到。

【样例解释三】
可以被抢救的点为:2,3,4,5,11,12。

【数据范围】
本题采用捆绑测试
\(对于 10\% 的数据,2 \leq n,m \leq 20\)
\(对于 30\% 的数据,2 \leq n \leq 2000,1 \leq m \leq 10000\)
\(对于 70\% 的数据,2 \leq n \leq 2 \times 10^5\)
\(对于 100\% 的数据,2 \leq n,m \leq 5 \times 10^6\)

题目解析

首先是求出每个节点能够成功返回\(S\)的最晚到达时间\(deadLine\),通过最短路径算法,求出毒气到达每个节点的最短时间,作为初始的\(deadLine\)

然后是从\(S\)点的临近节点开始,更新\(deadLine\)。求出每个节点最小的截止时间

因为从\(S\)临近节点开始,随着更新过程的推进,队列中各个节点的\(deadLine\)单调不减。又因为\(n\)的最大值\(5*10^6\),使用优先队列BFS更新\(O(nlogn)\)效率不够。所以考虑直接用以\(deadLine\)值为下标的vector代替有限队列,更新时枚举\(deadLine\)值从小到大更新vector,从而实现\(O(n+m)\)复杂度

最后是求出\(S\)到达各个节点的最短路径,判断是否能在截止时间前到达该节点

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include<queue>
#include<vector>
using namespace std;
char gc() {
  static char now[1 << 20], *S, *T;
  if (T == S) {
    T = (S = now) + std::fread(now, 1, 1 << 20, stdin);
    if (T == S) return EOF;
  }
  return *S++;
}
template <typename T>
void Read(T &x) {
  x = 0;
  char c = gc();
  while (c < '0' || c > '9') c = gc();
  x = c - '0';
  while ((c = gc()) >= '0' && c <= '9') x = x * 10 + c - '0';
}
template <typename T, typename... Args>
void Read(T &x, Args &... args) {
  Read(x);
  Read(args...);
}

struct Edge{
  int next,to;
}edge[10000005];
int head[5000005],num=0;
int n,m,vis[5000005],S,T,deadline[5000005],dis[5000005];
int q[10000005],h,t,Max;
vector<int> vec[5000005];
void add(int u,int v){
  num++;
  edge[num].next=head[u];
  head[u]=num;
  edge[num].to=v;
}
void getDis(){
    memset(dis,127/2,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[S]=0;
    h=0,t=1;
    q[h]=S;
    vis[S]=1;
    while (h<t){
      int u=q[h++];
      for (int i=head[u];i;i=edge[i].next){
        int v=edge[i].to;
        if (vis[v]) continue;
        dis[v]=dis[u]+1;
        q[t++]=v;
        vis[v]=1;
      }
    }
}
void getDeadLine(){
    memset(deadline,127/2,sizeof(deadline));
    Max=deadline[0];
    memset(vis,0,sizeof(vis));
    deadline[T]=0;
    h=0,t=1;
    q[h]=T;
    vis[T]=1;
    while (h<t){
      int u=q[h++];
      for (int i=head[u];i;i=edge[i].next){
        int v=edge[i].to;
        if (vis[v]||v==S) continue;
        deadline[v]=deadline[u]+1;
        q[t++]=v;
        vis[v]=1;
      }
    }
}
void calDeadLine(){
  memset(vis,0,sizeof(vis));
  int maxdead=0;
  vis[S]=1;
  for (int i=head[S];i;i=edge[i].next){
      int v=edge[i].to;
      if (deadline[v]==Max) continue;
      vec[deadline[v]].push_back(v);
      maxdead=(maxdead<deadline[v])?deadline[v]:maxdead;
      vis[v]=1;
  } 
  for (int i=maxdead;i;i--){
    for (int j=0;j<vec[i].size();j++){
      int u=vec[i][j];
      for (int k=head[u];k;k=edge[k].next){
        int v=edge[k].to;
        if (vis[v]) continue;
        deadline[v]=(deadline[v]<deadline[u]-1)?deadline[v]:deadline[u]-1;
        vis[v]=1;
        vec[deadline[v]].push_back(v);
      }
    }
  }
}
int main(){
  int u,v;
    Read(n,m);
    for (int i=0;i<m;i++){
      Read(u,v);
      add(u,v);add(v,u);
    }
    Read(S,T);
    getDis();
    //cout<<"getDis"<<endl;
    getDeadLine();
    //cout<<"getDeadLine"<<endl;
    calDeadLine();
    //cout<<"calDeadLine"<<endl;
    int ans=0;
    for (int i=1;i<=n;i++){
      //cout<<deadline[i]<<endl;
      if (i!=S&&dis[i]<deadline[i]&&(vis[i]||deadline[i]==Max)) ans++;
    }
    cout<<ans<<endl;
}
posted @ 2021-08-11 20:55  Z-Y-Y-S  阅读(34)  评论(0编辑  收藏  举报