潜入苏拉玛(BFS+构造)
问题 A: 潜入苏拉玛
时间限制: 1 Sec 内存限制: 128 MB
题目描述
你接到了⼀个任务,让你潜⼊苏拉玛城,和线⼈取得联络。苏拉玛的地图是⼀张N个点M条边的⽆向图,每个点表⽰苏拉玛城的⼀个路⼜,每条边表⽰苏拉玛内的⼀条道路,长度都是1。你从S号节点出发,线⼈在T号节点。
由于苏拉玛城内都是夜之⼦的哨兵,你需要假⾯伪装才能在苏拉玛的道路上⾏⾛。你的伪装只能坚持你⾛完K段道路不被发现,幸好在苏拉玛城内还有P个内应(分别在节点ai上),他们可以修复你的伪装,让它恢复到刚开始的状态。
你想知道K⾄少需要是多少,才能让你成功找到线⼈。
输入
第⼀⾏⼀个正整数CAS表⽰测试数据组数。
每组测试数据第⼀⾏有三个数N,M,P如上⽂所述,第⼆⾏P个数表⽰内应的位置。接下来M⾏,每⾏2个数表⽰⼀条边。最后⼀⾏S和T表⽰你开始的节点和你的⽬标节点。
输出
对于每组数据:如果你⽆法到达线⼈那⾥,输出-1,否则输出最⼩的K。
样例输入
2
6 6 3
1 3 6
1 2
2 3
4 2
5 6
4 5
3 4
1 6
7 10 3
1 3 4
1 2
4 2
7 5
4 5
7 1
2 5
7 2
3 7
3 2
5 1
4 6
样例输出
3
-1
提示
对于100%的数据,1≤K,S,T≤N≤100000,1≤M≤150000,1≤CAS≤5。
思路:
把起点、终点、内应都视为原点。
假设答案一定是偶数,也就是从\(S\)到\(T\)经过两个相邻原点的距离都\(\le 2K\)。
这样问题就转化为从每个原点出发\(bfs\) 各走\(k\)步,走出的点集是\(S,T\)联通。
如果\(S=T,ans=0\)。否则,
每次将相邻的点加入,重新\(bfs\),相当于\(ans+=2\)。
但是答案显然有奇数的情况,我们只需在每条边中间加一条中间点,也就是说如果\(i-j\)有一条边,改为\(i-x,x-j\)个有一条边。这样就保证了结果一定是偶数,最后把答案\(\div 2\)即可。
注意到是无向图,判连通性可以用并查集,加上线性的遍历,最终复杂度就是\(O(nlogn)\)。
//#pragma GCC optimize(2)
//#pragma GCC optimize(3, "Ofast", "inline")
#include "bits/stdc++.h"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,ll> pii;
const int N=1e6+5;
const ll mod=1e9+7;
const double eps=1e-5;
//const double pi=acos(-1);
vector<int>g[N];
int vis[N],f[N];
int getf(int x)
{
return f[x]==x?x:f[x]=getf(f[x]);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
int _;
cin>>_;
while(_--)
{
int n,m,k;
cin>>n>>m>>k;
queue<int>q,p;
memset(vis,0,sizeof vis);
for(int i=1;i<=m*2;i++) g[i].clear(),f[i]=i;
for(int j=0,x;j<k;j++)
{
cin>>x;
if(!vis[x])
q.push(x),vis[x]=1;
}
k=n+1;
while(m--)
{
int u,v;
cin>>u>>v;
g[u].push_back(k);
g[k].push_back(u);
g[k].push_back(v);
g[v].push_back(k);
k++;
}
int x,y;
cin>>x>>y;
if(!vis[x]) q.push(x),vis[x]=1;
if(!vis[y]) q.push(y),vis[y]=1;
int ans=0;
while(1)
{
ans++;
if(q.empty())
{
ans=-1;break;
}
while(!q.empty())
{
int u=q.front();q.pop();
for(auto v:g[u])
{
int uu=getf(u),vv=getf(v);
if(uu>vv) swap(uu,vv);
f[vv]=uu;
if(vis[v]) continue;
p.push(v);
vis[v]=1;
}
}
if(getf(x)==getf(y))
break;
while(!p.empty())
{
q.push(p.front());p.pop();
}
}
cout<<ans<<'\n';
}
return 0;
}