「CF1149D」Abandoning Roads

题目

点这里看题目。


给定一张 \(n\) 个结点 \(m\) 条边的无向带边权简单连通图 \(G\),且图上边权要么为 \(a\),要么为 \(b\)(保证 \(a\le b\)

对于每个结点 \(x\),求所有的最小生成树中,从 \(1\) 出发到 \(x\) 的路径的权值和的最小值。

所有数据满足 \(1\le n\le 70,1\le m\le \min\{200, \binom{n}{2}\},1\le a\le b\le 10^7\)

分析

看到 \(n\le 70,m\le 200\),自动跳转到多项式复杂度。

结果,数据小了什么幺蛾子都能整出来。


由于边权只有两种,于是结合 Kruskal 的算法流程,可以想到首先所有 \(a\) 边连起来,并将连通块缩成点。这样再来考虑 \(b\) 边时,就可以发现合法的路径要求在缩点后的图上,\(b\) 边构成的是简单路径

这条件看起来简单,但是实现起来难。常规的最短路可以保证“简单性”,是因为非简单路径一定不优。但这一点在缩点过后的图上一定不成立。从另一方面来说,改造最短路算法也应该是不现实的。

当我在“改造最短路”的歧途上渐行渐远时,也许仅仅有两句至理“名言”可以帮助我了:

  1. 如果 OI 题中有什么路子看不到解决的可能性,那它多半是错的

  2. 不知道怎么办的时候,先思考暴力,先看部分分

暴力的方法看起来很容易,那就是做一个状压 DP,用 \(f_{S,u}\) 表示当前在 \(u\) 点,且已经经过的 \(a\) 连通块的编号为 \(S\) 时的最短路。很显然它可以用最短路算法进行转移。

现在,它的复杂度是 \(O(2^nn)\) 或者 \(O(2^nn^2)\),显然还不是什么可以直接通过的算法。当我们尝试优化它时,我们首先想干掉的就是 \(2^n\)

如果我们无法改变图的结构来去掉 \(2^n\),那我们只好尝试减少需要记录的信息的数目,也就是降指数。当我们注意到这一点时,我们就可以开始研究“什么情况下可以不记录是否到过某个 \(a\) 连通块”。

首先,如果 \(a\) 连通块大小为 \(1\),是个单点,则它显然不需要记录。如果 \(a\) 连通块大小为 \(2\),则出现不合法情况时,必然是从一个点经过两条 \(b\) 边走到另一个点,然而这比直接走一条 \(a\) 边更劣。进一步地,大小为 \(3\) 也可以不记录。所以,实际上需要记录的 \(a\) 连通块大小必须 \(\ge 4\)。算一算,需要记录的连通块数量 \(\le \lfloor\frac n 4\rfloor\le 17\),放到指数上刚好合适。

经过精简之后,复杂度就变成了 \(O(2^{\frac n 4}n^2)\),可以通过了。

代码

#include <queue>
#include <cstdio>

#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )

const int INF = 1e9;
const int MAXN = 75, MAXM = 205, MAXS = ( 1 << 17 ) + 5;

template<typename _T>
inline void Read( _T &x ) {
    x = 0; char s = getchar(); bool f = false;
    while( s < '0' || '9' < s ) { f = s == '-', s = getchar(); }
    while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
    if( f ) x = -x;
}

template<typename _T>
inline void Write( _T x ) {
    if( x < 0 ) putchar( '-' ), x = -x;
    if( 9 < x ) Write( x / 10 );
    putchar( x % 10 + '0' );
}

template<typename _T>
inline _T Min( const _T &a, const _T &b ) {
    return a < b ? a : b;
}

template<typename _T>
inline _T Max( const _T &a, const _T &b ) {
    return a > b ? a : b;
}

typedef std :: pair<int, int> State;
typedef std :: pair<int, State> Node;

std :: priority_queue<Node, std :: vector<Node>, std :: greater<Node> > q;

int dp[MAXS][MAXN];
bool vis[MAXS][MAXN];

int grph[MAXN][MAXN];

int nwId[MAXN], ID = 0;
int bel[MAXN], siz[MAXN], tot = 0;
int fa[MAXN];

int N, M, A, B;

inline void MakeSet( const int &n ) {
    rep( i, 1, n ) fa[i] = i;
}

int FindSet( const int &u ) {
    return fa[u] == u ? u : ( fa[u] = FindSet( fa[u] ) );
}

inline void UnionSet( const int &u, const int &v ) {
    fa[FindSet( u )] = FindSet( v );
}

int main() {
    Read( N ), Read( M ), Read( A ), Read( B );
    rep( i, 1, N ) rep( j, 1, N )
        grph[i][j] = i == j ? 0 : INF;
    MakeSet( N );
    rep( i, 1, M ) {
        int u, v, w;
        Read( u ), Read( v ), Read( w );
        if( w == A ) UnionSet( u, v );
        grph[u][v] = Min( grph[u][v], w );
        grph[v][u] = Min( grph[v][u], w );
    }
    rep( i, 1, N ) if( fa[i] == i ) bel[i] = ++ tot;
    rep( i, 1, N ) siz[bel[i] = bel[FindSet( i )]] ++;
    rep( i, 1, tot ) if( siz[i] >= 4 ) nwId[i] = ID ++;

    int U = 1 << ID;
    rep( S, 0, U - 1 ) rep( j, 1, N ) 
        dp[S][j] = INF, vis[S][j] = false;
    if( siz[bel[1]] >= 4 ) {
        int S = 1 << nwId[bel[1]];
        q.push( { dp[S][1] = 0, { S, 1 } } );
    } else 
        q.push( { dp[0][1] = 0, { 0, 1 } } );
    while( ! q.empty() ) {
        int S = q.top().second.first,
            u = q.top().second.second; q.pop();
        if( vis[S][u] ) { continue; } vis[S][u] = true;
        rep( v, 1, N ) 
            if( u ^ v && grph[u][v] < INF ) {
                int T = S;
                if( grph[u][v] == B ) {
                    if( bel[u] == bel[v] ) continue;
                    if( siz[bel[v]] >= 4 ) {
                        if( T >> nwId[bel[v]] & 1 ) continue;
                        T |= 1 << nwId[bel[v]];
                    }
                }
                if( ! vis[T][v] && dp[T][v] > dp[S][u] + grph[u][v] )
                    q.push( { dp[T][v] = dp[S][u] + grph[u][v], { T, v } } );
            }
    }
    rep( i, 1, N ) {
        int ans = INF;
        rep( S, 0, U - 1 )
            ans = Min( ans, dp[S][i] );
        Write( ans ), putchar( " \n"[i == N] );
    }
    return 0;
}
posted @ 2022-11-23 21:53  crashed  阅读(57)  评论(0编辑  收藏  举报