抢救实验数据
题目背景
某大型实验中心的一个实验室发生了毒气泄露,现在实验员想要抢救实验数据。
题目描述
实验中心可以看做一个 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;
}