题目大意
在比特镇一共有n 个街区,编号依次为1 到n,它们之间通过若干条单向道路连接。
比特镇的交通系统极具特色,除了m 条单向道路之外,每个街区还有一个编码vali,不同街区可能拥有相同的编码。如果val_i and val_j = val_j,即val_i 在二进制下与val_j 做与运算等于val_j,那么也会存在一条额外的从i 出发到j 的单向道路。
Byteasar 现在位于1 号街区,他想知道通过这些道路到达每一个街区最少需要多少时间。因为比特镇的交通十分发达,你可以认为通过每条道路都只需要1 单位时间。
n≤200000 m≤300000 0≤vali≤ 220
分析
直接连边显然会超时,考虑优化构图。
如果给每一个权值都建一个点,然后一个权值向能到达的其它权值连一条长度为0的边。接下来,每个街区向它的权值连一条长度为0的边,权值向街区连一条长度为1的边。显然跑出来的最短路是等价的。
对呀,等价的!
但是如果向能到达的其它权值都连边,就太多边了。只需向二进制下比它少一个1的连边即可。
因为边权只有0、1,跑最短路用bfs即可。注意:要先扩展以权值为0的边相连的点,并且是队列里dis[]相同的点一起扩展,这样才能保证队列中最短路长度是递增的。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int M=1<<20,N=200005,maxn=N+M;
typedef long long LL;
int val[N],tot,dis[maxn],Data[maxn],n,m,h[maxn],e[maxn],nxt[maxn];
bool v[maxn];
char c;
int read()
{
for (c=getchar();c<'0' || c>'9';c=getchar());
int x=c-48;
for (c=getchar();c>='0' && c<='9';c=getchar()) x=x*10+c-48;
return x;
}
int lowbit(int x)
{
return x&-x;
}
void add(int x,int y)
{
e[++tot]=y; nxt[tot]=h[x]; h[x]=tot;
}
void put(int x)
{
if (!x) return;
int i,j;
for (i=x;i;i^=j)
{
j=lowbit(i);
if (!v[x^j])
{
Data[++m]=x^j;
v[x^j]=1;
dis[x^j]=dis[x];
put(x^j);
}
}
}
int main()
{
freopen("walk.in","r",stdin); freopen("walk.out","w",stdout);
n=read(); m=read();
for (int i=1;i<=n;i++)
{
val[i]=read();
add(val[i],i+M);
}
while (m--)
{
int x=read(),y=read();
add(x+M,y+M);
}
Data[m=1]=M+1; v[M+1]=1;
memset(dis,255,sizeof(dis));
dis[M+1]=0;
for (int i=1;i<=m;i++)
{
int j;
for (j=i;j<=m && dis[Data[i]]==dis[Data[j]];j++);
for (int k=i;k<j;k++)
{
int x=Data[k];
if (x>M)
{
if (!v[val[x-M]])
{
Data[++m]=val[x-M];
v[Data[m]]=1;
dis[Data[m]]=dis[x];
put(Data[m]);
}
}else put(x);
}
for (int k=i;k<j;k++)
{
int x=Data[k];
for (int i=h[x];i;i=nxt[i]) if (!v[e[i]])
{
v[e[i]]=1;
Data[++m]=e[i];
dis[e[i]]=dis[x]+1;
}
}
i=j-1;
}
for (int i=1;i<=n;i++) printf("%d\n",dis[i+M]);
fclose(stdin); fclose(stdout);
return 0;
}