bzoj 2096 [POI2004]ZAW——二进制枚举
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2069
可以把直接相连的点分成 从1点出的一部分 和 走向1点的一部分。多起点最短路就和正常的差不多。
怎么分才能不漏掉答案?按点的编号二进制每一位是0还是1分成两部分。因为两两编号不同,所以每两个总有一次被分到不同的集合。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int N=5005,M=1e4+5,lm=17; int n,m,hd[N],xnt=1,to[M<<1],nxt[M<<1],w[M<<1]; int fr[N],cnt,dis[N],ans=0x3f3f3f3f; bool vis[N],st[N]; priority_queue<pair<int,int> > q; void add(int x,int y,int z0,int z1) { to[++xnt]=y;nxt[xnt]=hd[x];w[xnt]=z0;hd[x]=xnt; to[++xnt]=x;nxt[xnt]=hd[y];w[xnt]=z1;hd[y]=xnt; if(x==1)fr[++cnt]=xnt-1;if(y==1)fr[++cnt]=xnt; } void dj() { memset(dis,0x3f,sizeof dis); memset(vis,0,sizeof vis);vis[1]=1; for(int i=1;i<=cnt;i++) if(st[i]) dis[to[fr[i]]]=w[fr[i]],q.push(make_pair(-dis[to[fr[i]]],to[fr[i]])); while(q.size()) { int k=q.top().second;q.pop(); while(vis[k]&&q.size())k=q.top().second,q.pop(); if(vis[k])break;vis[k]=1; for(int i=hd[k],v;i;i=nxt[i]) if(!vis[v=to[i]]&&dis[v]>dis[k]+w[i]) dis[v]=dis[k]+w[i],q.push(make_pair(-dis[v],v)); } } int main() { scanf("%d%d",&n,&m); for(int i=1,x,y,z0,z1;i<=m;i++) { scanf("%d%d%d%d",&x,&y,&z0,&z1); add(x,y,z0,z1); } for(int i=0;i<=lm;i++) { int bin=(1<<i); for(int j=1;j<=cnt;j++) if(to[fr[j]]&bin) st[j]=1; else st[j]=0; dj(); for(int j=1;j<=cnt;j++) { if(!st[j]) ans=min(ans,dis[to[fr[j]]]+w[fr[j]^1]); st[j]=!st[j]; } dj(); for(int j=1;j<=cnt;j++) if(!st[j]) ans=min(ans,dis[to[fr[j]]]+w[fr[j]^1]); } printf("%d\n",ans); return 0; }