根据kruskal的贪心过程,先将所有$a$类边连起来,对于一个连通块内的两点,必然通过$a$边联通
考虑对于一条最短路径,必然是一段(可能为空)$a$类边+1条$b$类边,同时其合法当且仅当这些$b$类边都能被加入最小生成树中,即不会与$a$类边产生环,又即不重复经过一个连通块
状压之前经过的连通块求最短路(当使用$b$类边离开后再加入),那么时间复杂度为$o(2^{n}m)$
注意到我们是在求最短路径,若一个连通块点数小于4,则必然从连通块内部走,因此复杂度变为$o(2^{\frac{n}{4}}m)$
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 105 4 struct ji{ 5 int nex,to,len; 6 }edge[N<<2]; 7 queue<pair<int,int> >q; 8 int E,n,m,a,b,x,y,z,head[N],f[N],sz[N],bl[N],vis[N][200005],d[N][200005]; 9 int find(int k){ 10 if (k==f[k])return k; 11 return f[k]=find(f[k]); 12 } 13 void merge(int x,int y){ 14 x=find(x),y=find(y); 15 if (x!=y){ 16 f[x]=y; 17 sz[y]+=sz[x]; 18 } 19 } 20 void add(int x,int y,int z){ 21 edge[E].nex=head[x]; 22 edge[E].to=y; 23 edge[E].len=z; 24 head[x]=E++; 25 } 26 void spfa(){ 27 memset(d,0x3f,sizeof(d)); 28 d[1][0]=0; 29 vis[1][0]=1; 30 q.push(make_pair(1,0)); 31 while (!q.empty()){ 32 int k=q.front().first,s=q.front().second; 33 q.pop(); 34 for(int i=head[k];i!=-1;i=edge[i].nex){ 35 int v=edge[i].to,ss=s; 36 if (edge[i].len==b)ss|=bl[k]; 37 if (((ss&bl[v])==0)&&(d[v][ss]>d[k][s]+edge[i].len)){ 38 d[v][ss]=d[k][s]+edge[i].len; 39 if (!vis[v][ss]){ 40 vis[v][ss]=1; 41 q.push(make_pair(v,ss)); 42 } 43 } 44 } 45 vis[k][s]=0; 46 } 47 } 48 int main(){ 49 scanf("%d%d%d%d",&n,&m,&a,&b); 50 memset(head,-1,sizeof(head)); 51 for(int i=1;i<=n;i++){ 52 f[i]=i; 53 sz[i]=1; 54 } 55 for(int i=1;i<=m;i++){ 56 scanf("%d%d%d",&x,&y,&z); 57 if (z==a)merge(x,y); 58 add(x,y,z); 59 add(y,x,z); 60 } 61 m=0; 62 for(int i=1;i<=n;i++) 63 if ((f[i]==i)&&(sz[i]>=4))bl[i]=(1<<m++); 64 for(int i=1;i<=n;i++)bl[i]=bl[find(i)]; 65 spfa(); 66 for(int i=1;i<=n;i++){ 67 int ans=0x3f3f3f3f; 68 for(int j=0;j<(1<<m);j++)ans=min(ans,d[i][j]); 69 printf("%d ",ans); 70 } 71 }View Code