bzoj 4398 福慧双修 —— 二进制分组+多起点最短路
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4398
按二进制每一位是 0/1 把 1 号点的儿子分成两组,分别作为起点和终点跑多起点最短路,最优解的起点和终点总有一次会被分到不同组里;
太久没写 dijkstra 竟然WA了4次...别忘了 priority_queue 是大根堆-_-,还要注意循环计数的 i,j 不要重了...
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; int const xn=40005,xm=1e5+5,inf=0x3f3f3f3f; int n,m,hd[xn],ct,nxt[xm<<1],to[xm<<1],w[xm<<1],dis[xn],ans; bool vis[xn],in[xn],out[xn]; struct N{ int d,id; bool operator < (const N &y) const {return d>y.d;}//> }; priority_queue<N>q; int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar(); return f?ret:-ret; } void add(int x,int y,int z){to[++ct]=y; nxt[ct]=hd[x]; w[ct]=z; hd[x]=ct;} void dij() { memset(vis,0,sizeof vis); while(q.size()) { int x=q.top().id; q.pop(); if(vis[x])continue; vis[x]=1; for(int i=hd[x],u;i;i=nxt[i]) { if(vis[u=to[i]]||(u==1&&out[x])||(x==1&&in[u]))continue; if(dis[u]>dis[x]+w[i]) dis[u]=dis[x]+w[i],q.push((N){dis[u],u}); } } ans=min(ans,dis[1]); } int main() { n=rd(); m=rd(); for(int i=1,x,y,z,k;i<=m;i++) { x=rd(); y=rd(); z=rd(); k=rd(); add(x,y,z); add(y,x,k); } int cnt=0,t=n; while(t)cnt++,t/=2; ans=inf; for(int i=0;i<cnt;i++) { memset(dis,0x3f,sizeof dis); while(q.size())q.pop(); for(int j=hd[1],u;j;j=nxt[j]) { in[u=to[j]]=0; out[u]=0; if(u&(1<<i))dis[u]=w[j],out[u]=1,q.push((N){w[j],u}); else in[u]=1; } dij(); memset(dis,0x3f,sizeof dis); while(q.size())q.pop(); for(int j=hd[1],u;j;j=nxt[j]) { if(in[u=to[j]])in[u]=0,out[u]=1,dis[u]=w[j],q.push((N){w[j],u}); else out[u]=0,in[u]=1; } dij(); } printf("%d\n",(ans==inf?-1:ans)); return 0; }