1. 程式人生 > 其它 >Solution -「Gym 102979L」 Lights On The Road

Solution -「Gym 102979L」 Lights On The Road

\(\mathcal{Description}\)

  Link.

  給定序列 \(\{w_n\}\),選擇 \(i\) 位置的代價為 \(w_i\),要求每個位置要不被選擇,要不左右兩個位置至少被選擇一個。求前 \(k\) 小的選擇代價。

  \(n,k\le2.5\times10^5\)

\(\mathcal{Solution}\)

  建圖,邊形如 \(\lang i,i+j,w_i\rang~(j=1,2,3)\),再引入左右兩個虛點 \(s,t\),那麼每種方案對應從 \(s\)\(t\) 的一條路徑,題目即求無負權圖上的前 \(k\) 小路徑,是道模板題,很可惜我並不會。(

  還是用前 \(k\) 小的一貫套路:用堆維護,每次取當前最優方案擴充套件。首先在建出以 \(t\) 為根的內向最短路樹,考慮用一條非樹邊 \(\lang u,v,w\rang\) 替換最短路 \(P=\lang s,\cdots,t\rang\) 的一段,則有 \(P'=\lang s,\cdots,u,v,\cdots,t\rang\),且滿足 \(w(P')=d_S-d_u+w+d_v\),其中 \(d_u\)\(u\)\(t\) 的最短路。這樣我們得到了擴充套件一次的方法:取 \(s\)\(t\) 最短路的鄰接邊 \(\lang u,v,w\rang\) 中,\(d_v+w-d_u\)

最小的邊替換最短路。

  如果替換多條邊呢?可以發現有兩種替換方法:

  • 將當前最優的 \(\lang u,v,w\rang\) 替換為新的 \(\lang u,v',w'\rang\)
  • 在當前 \(\lang u,v,w\rang\) 的基礎上,取 \(v\)\(t\) 最短路的鄰接邊擴充套件。

  那麼用可持久化左偏樹維護每個點到 \(t\) 的可用鄰接邊集合即可支援擴充套件。設圖的點數為 \(n\),邊數為 \(m\),整個複雜度則為 \(\mathcal O((m+k)\log m)\),本題則有 \(\mathcal O((n+k)\log n)\)

\(\mathcal{Code}\)

/*~Rainybunny~*/

#include <bits/stdc++.h>

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

typedef long long LL;
typedef std::pair<LL, int> PLI;
typedef std::pair<int, int> PII;
#define fi first
#define se second

const int MAXN = 2.5e5;
int n, m, pre[MAXN + 5], root[MAXN + 5];
LL dis[MAXN + 5];
std::vector<PII> oriG[MAXN + 5], revG[MAXN + 5];

struct PersistentLeftistTree {
    static const int MAXND = 3e7;
    int node, ch[MAXND + 5][2], rds[MAXND + 5];
    PLI val[MAXND + 5]; // (cost, end point).

    inline int newnd( const PLI& v ) {
        val[++node] = v, ch[node][0] = ch[node][1] = 0, rds[node] = 1;
        return node;
    }

    inline int merge( int x, int y ) {
        if ( !x || !y ) return x | y;
        if ( val[x] > val[y] ) std::swap( x, y );
        int u = newnd( val[x] );
        ch[u][0] = ch[x][0], ch[u][1] = merge( ch[x][1], y );
        if ( rds[ch[u][1]] > rds[ch[u][0]] ) std::swap( ch[u][0], ch[u][1] );
        return rds[u] = rds[ch[u][1]] + 1, u;
    }
} plt;

inline void link( const int s, const int t, const int w ) {
    oriG[s].push_back( { t, w } ), revG[t].push_back( { s, w } );
    // std::cerr << s << ' ' << t << ' ' << w << '\n';
}

inline void dijkstra() {
    static std::priority_queue<PLI, std::vector<PLI>, std::greater<PLI> > heap;
    memset( dis, 0x3f, sizeof dis );
    heap.push( { dis[n + 1] = 0, n + 1 } );
    while ( !heap.empty() ) {
        PLI p( heap.top() ); heap.pop();
        if ( dis[p.se] < p.fi ) continue;
        for ( const PII& e: revG[p.se] ) {
            if ( dis[e.fi] > p.fi + e.se ) {
                heap.push( { dis[e.fi] = p.fi + e.se, e.fi } );
                pre[e.fi] = p.se;
            }
        }
    }
}

inline void solve() {
    per ( u, n, 0 ) {
        root[u] = root[pre[u]];
        for ( const PII& v: oriG[u] ) {
            if ( pre[u] != v.fi ) {
                root[u] = plt.merge( root[u],
                  plt.newnd( { dis[v.fi] + v.se - dis[u], v.fi } ) );
            }
        }
    }

    static std::priority_queue<PLI, std::vector<PLI>, std::greater<PLI> > heap;
    std::cout << dis[0] << '\n';
    if ( root[0] ) heap.push( { plt.val[root[0]].fi, root[0] } );
    while ( --m ) {
        if ( heap.empty() ) { std::cout << "-1\n"; continue; }
        PLI p( heap.top() ); heap.pop();
        std::cout << dis[0] + p.fi << '\n';

        int u;
        if ( ( u = plt.merge( plt.ch[p.se][0], plt.ch[p.se][1] ) ) ) {
            heap.push( { p.fi - plt.val[p.se].fi + plt.val[u].fi, u } );
        }
        if ( ( u = root[plt.val[p.se].se] ) ) {
            heap.push( { p.fi + plt.val[u].fi, u } );
        }
    }
}

int main() {
    std::ios::sync_with_stdio( false ), std::cin.tie( 0 );

    std::cin >> n >> m;
    rep ( i, 1, n ) {
        int w; std::cin >> w;
        rep ( j, 1, 3 ) {
            if ( i + j <= n || ( i + j == n + 1 && j < 3 ) ) {
                link( i, i + j, w );
            }
        }
    }
    link( 0, 1, 0 );
    if ( n > 1 ) link( 0, 2, 0 );

    dijkstra();
    // rep ( i, 0, n + 1 ) std::cout << dis[i] << ' ' << pre[i] <<  '\n';
    solve();
    return 0;
}