CF1149D Abandoning Roads
Solution
可以看出的是,a 边所形成的连通块是固定的,然后 b 边的作用是把这些连通块串起来。
那么一条路径合法当且仅当不存在从一个 a 连通块通过 b 边出去然后又回来。正确性显然。
那么我们可以考虑一个暴力 dp 即是 \(f_{S,u}\) 表示已经走过了 \(S\) 这个集合里的 a 连通块,当前在 \(u\) 的最小代价。可以发现这个东西可以用最短路优化。
但是 \(n\le 70\),显然需要进一步的优化。可以看出的是,对于 \(\le 3\) 的 a 连通块,最优策略不可能走出去再走回来,因为这样至少需要 \(2\) 条 b 边,但是你在 a 连通块内最多也就走 \(2\) 条 a 边,所以我们最多只有 \(\lfloor\frac{n}{4}\rfloor\) 个连通块,就可以做了。
似乎做完之后再看就不是很难,可以一旦自己做就什么都想不到了?/kk
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define MAXN 205
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}
int n,m,A,B,tot,fa[MAXN];
int findSet (int x){return fa[x] == x ? x : fa[x] = findSet (fa[x]);}
struct edge{
int u,v;
}s[MAXN];
vector <int> g[MAXN],H[MAXN];
vector <int> Seq;
bool vis[1 << 17][MAXN];
int cnt,ind[MAXN],tur[MAXN],bak[MAXN],siz[MAXN],dis[1 << 17][MAXN];
#define pii pair<int,int>
#define se second
#define fi first
struct node{
int dis,Sta,pos;
bool operator < (const node &p)const{return dis > p.dis;}
};
void Dijkstra (){
int sta = bak[ind[findSet (1)]] ? (1 << bak[ind[findSet (1)]] - 1) : 0;
priority_queue <node> q;memset (dis,0x3f,sizeof (dis)),dis[sta][1] = 0,q.push (node{0,sta,1});
while (!q.empty()){
node it = q.top();q.pop ();
int Sta = it.Sta,u = it.pos,d = it.dis;
if (vis[Sta][u] || d != dis[Sta][u]) continue;
vis[Sta][u] = 1;
for (Int v : H[u]){
if (dis[Sta][v] > dis[Sta][u] + A){
dis[Sta][v] = dis[Sta][u] + A;
if (!vis[Sta][v]) q.push (node{dis[Sta][v],Sta,v});
}
}
for (Int v : g[u]){
int nid = bak[ind[findSet (v)]],nSta = nid ? (Sta | (1 << nid - 1)) : Sta;
if (nid && (Sta >> nid - 1 & 1)) continue;
if (findSet (v) == findSet (u)) continue;
if (dis[nSta][v] > dis[Sta][u] + B){
dis[nSta][v] = dis[Sta][u] + B;
if (!vis[nSta][v]) q.push (node{dis[nSta][v],nSta,v});
}
}
}
}
signed main(){
read (n,m,A,B);
for (Int x = 1;x <= n;++ x) fa[x] = x;
for (Int i = 1;i <= m;++ i){
int u,v,w;read (u,v,w);
if (w == A) s[++ tot] = edge{u,v},H[u].push_back (v),H[v].push_back (u);
else g[u].push_back (v),g[v].push_back (u);
}
for (Int i = 1;i <= tot;++ i){
int u = s[i].u,v = s[i].v;
if (findSet (u) == findSet (v)) ;
else fa[fa[u]] = fa[v];
}
for (Int u = 1;u <= n;++ u) if (fa[u] == u) ind[u] = ++ cnt,tur[cnt] = u;
for (Int u = 1;u <= n;++ u) siz[ind[findSet (u)]] ++;
for (Int i = 1;i <= cnt;++ i) if (siz[i] >= 4) Seq.push_back (i),bak[i] = Seq.size();
Dijkstra ();
for (Int i = 1;i <= n;++ i){
int ans = 1e9;
for (Int S = 0;S < (1 << Seq.size());++ S) chkmin (ans,dis[S][i]);
write (ans),putchar (' ');
}
putchar ('\n');
return 0;
}