您的位置:首页 > 其它

JZOJ4858. 【GDOI2017模拟11.4】Walk

2016-11-04 16:15 323 查看

题目描述

在比特镇一共有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,val_i<=220

分析

首先可以发现,边权只有1,那么直接bfs不用最短路。

怎么处理额外路径呢?暴力连边是n2的,然而我们发现,假设已经处理了点i的最短路,那么所有没有处理过的,满足and条件的点j都会被处理出来嘛。那么满足and条件的点就是满足在二进制下a[j]是a[i]删掉一些1的结果。同时我们又注意到,相同的a[j]处理一次就够了,如何避免a[j]被处理多次呢?

构建虚拟点,编号n+1~n+max(val_i),表示当真实点的权值为某个值x时,它的最短路是多少。那么我们把这些点加进图里面,每次走一条边就相当于删掉二进制下某个1,一起跑就行了,强制当真实点进入虚拟点要1的花费,出去则不用。

有一些细节,就是虚点优先跑,因为边权为0嘛。另外,如果边要全部建出来,会爆空间。其实对于虚拟点不用存边,因为你知道删去一个1后会是哪个点,根本不用存边。

代码

#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
const int N=200005,M=300005,er=1048576;
int b[M*4],next[M*4],first[N+er],tt;
int dis[N+er],n,m,x,y,a
,i,j,k,l,dur,p,d[N+er],e[N+er],q1,q2,p1,p2,z,t;
void cr(int x,int y)
{
tt++;
b[tt]=y;
next[tt]=first[x];
first[x]=tt;
}
void bfs()
{
fo(i,1,n+er) dis[i]=-1;
dis[1]=0;
d[1]=1;
q1=0;
q2=1;
while (q1<q2)
{
x=d[++q1];
for(p=first[x];p;p=next[p])
if (dis[b[p]]==-1)
{
dis[b[p]]=dis[x]+1;
d[++q2]=b[p];
}
if (dis[a[x]+n]==-1)
{
dis[a[x]+n]=dis[x]+1;
e[1]=a[x]+n;
p1=0;
p2=1;
while (p1<p2)
{
z=y=e[++p1];
z-=n;
for(p=first[y];p;p=next[p])
if (dis[b[p]]==-1)
{
dis[b[p]]=dis[x]+1;
d[++q2]=b[p];
}
while (z)
{
t=y-((z)&(-z));
if (t==n) break;
if (dis[t]==-1)
{
dis[t]=dis[x]+1;
e[++p2]=t;
}
z-=z&(-z);
}
}
}
}
}
int main()
{
freopen("walk.in","r",stdin);
freopen("walk.out","w",stdout);
scanf("%d%d",&n,&m);
fo(i,1,n)
{
scanf("%d",a+i);
cr(a[i]+n,i);
}
fo(i,1,m)
{
scanf("%d%d",&x,&y);
cr(x,y);
}
bfs();
fo(i,1,n)
{
printf("%d\n",dis[i]);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: