题目描述
在比特镇一共有n 个街区,编号依次为1 到n,它们之间通过若干条单向道路连接。
比特镇的交通系统极具特色,除了m 条单向道路之外,每个街区还有一个编码vali,不同街区可能
拥有相同的编码。如果val_i and val_j = val_j,即val_i 在二进制下与val_j 做与运算等于val_j,那么也会
存在一条额外的从i 出发到j 的单向道路。
Byteasar 现在位于1 号街区,他想知道通过这些道路到达每一个街区最少需要多少时间。因为比特
镇的交通十分发达,你可以认为通过每条道路都只需要1 单位时间。
优化连边
我们可以暴力连边,然后广搜一遍,但显然要T。
我们尝试用分治法优化连边,不改变原图的等价性。
先对每种权值建一个点。
solve(l,r)表示把[l,r]内的边连了。
递归处理solve(l,mid)和solve(mid+1,r)
然后左右区间对应的权值连边,大的向小的连0。
对于每个原先的节点i,i向ai连0,ai向i连1。原本存在边的节点i和j,i向j连1。
易证这没有改变原图的等价性。
现在有0有1,那么每次一个元素加入队尾时,要把其所有连出去的0边没有访问过的元素也加入队尾。
为了优化常数,可以先连1边再连0边。
具体实现看代码。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=200000+10,maxd=20,maxtot=maxn+(1<<maxd)+10,maxm=300000+10,maxtop=11185800;
bool dis[maxtop];
int h[maxtot],next[maxtop],go[maxtop],f[maxtot],a[maxn];
int dl[maxtot];
int i,j,k,l,t,n,m,tot,top,head,tail,now;
int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
void add(int x,int y,int z){
go[++top]=y;
dis[top]=z;
next[top]=h[x];
h[x]=top;
}
void solve(int l,int r){
if (l==r) return;
int i,mid=(l+r)/2;
solve(l,mid);solve(mid+1,r);
fo(i,mid+1,r) add(n+i+1,n+(i-(mid+1-l))+1,0);
}
void insert(int x){
dl[++tail]=x;
int t=h[x];
while (t){
if (dis[t]==1) break;
if (f[go[t]]==-1){
f[go[t]]=f[x];
insert(go[t]);
}
t=next[t];
}
h[x]=t;
}
int main(){
freopen("walk.in","r",stdin);freopen("walk.out","w",stdout);
n=read();m=read();
tot=n+(1<<maxd);
fo(i,1,n) a[i]=read();
fo(i,1,m){
j=read();k=read();
add(j,k,1);
}
fo(i,1,n) add(n+a[i]+1,i,1);
fo(i,1,n) add(i,n+a[i]+1,0);
solve(0,(1<<maxd)-1);
fo(i,1,tot) f[i]=-1;
f[1]=0;
dl[tail=1]=1;
while (head<tail){
k=dl[++head];
t=h[k];
while (t){
if (f[go[t]]==-1){
f[go[t]]=f[k]+dis[t];
insert(go[t]);
}
t=next[t];
}
}
fo(i,1,n) printf("%d\n",f[i]);
}