「JOISC 2022」复兴计划

题目

点这里看题目。


给定一张 \(N\) 个点 \(M\) 条边的无向连通无自环图。一条边除其连接的端点外,还有参数 \(w\)

进行 \(Q\) 次询问,每次询问给定 \(x\),回答一条边的边权为 \(|x-w|\) 时最小生成树的代价之和。

\(100\%\) 的数据满足:\(1\le N\le 500, 1\le M\le 10^5,1\le Q\le 10^6\)

分析

注意!本题并不是考虑凸包!不要看到线性贡献看到 \(\min\) or \(\max\) 就开始往这方面思考

考虑边权 \(|x-W|\) 对于 Kruskal 算法带来的影响,很显然它会导致边的顺序改变。

为了避免边权相等,此处定义边的严格的序:首先比较边权,其次比较编号。此后,先定性分析:

  • 如果一条原始边权为 \(W\) 的边 \(e\) 可以出现在 \(x=x_0\) 时的最小生成树中,那么它一定也会出现在 \(x=W\) 时的最小生成树中

    因为相对而言,这条边排名一定不会变大。

  • 如果边 \(e\) 会出现在 \(x=W\) 时的最小生成树中,则使得它在最小生成树中的 \(x\) 必然构成一段区间

    这是因为,\(x\)\(W\) 出发向一个方向不断移动时,若有另一条原始边权为 \(W'\) 的边,\(|x-W|-|x-W'|\) 不会变大。所以排序后的序列中这条边只会不断后移。

我们只需要找出这个“区间”即可。不妨先考虑左边界。注意到 \(x<W\) 时,一条原始边权为 \(W'(W'>W)\) 的边一定排在 \(e\) 之后,所以只用考虑 \(W'<W\) 的边。

对于函数 \(y_1=|x-a|\)\(y_2=|x-b|\)\(a<b\))来说,\(y_1,y_2\) 的大小关系在 \(\frac{a+b}{2}\) 处分界。在这个情景中,\(b=W\),而 \(a=W'\)。考虑 \(x=0\) 时的排序情况,其中 \(e\) 居于第 \(p\) 位,则任意 \(x\) 时排序后,居于 \(e\) 之前的边必然是 \(x=0\) 序列中以 \(p-1\) 结尾的一段后缀(可为空后缀)。所以,我们相当于需要找到最大的 \(q\),使得“将 \(x=0\) 序列中 \([q,p-1]\) 位置的边加入图中后,\(e\) 的两个端点已经连通”。该问题可以通过扫描线+维护最大标号生成树解决。

所以,暴力计算可以得到 \(O(MN+Q)\) 的算法,而使用 LCT 维护则可得到 \(O(N+M\log N+Q)\) 的算法。

代码

#include <bits/stdc++.h>

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

typedef long long LL;

const int MAXN = 505, MAXM = 1e5 + 5, MAXQ = 1e6 + 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' );
}

struct Edge {
    int u, v, w, id;
};

Edge edg[MAXM];
int lef[MAXM], rig[MAXM], iniWei[MAXM];

std :: vector<std :: pair<int, int> > grph[MAXN];

LL coe1[MAXQ], coe2[MAXQ];
int arg[MAXQ];

int N, M, Q;

inline bool operator < ( const Edge &e1, const Edge &e2 ) {
    return e1.w == e2.w ? e1.id < e2.id : e1.w < e2.w;
}

inline void AddEdge( const int &u, const int &v, const int &id ) {
    grph[u].emplace_back( v, id ); 
    grph[v].emplace_back( u, id );
}

inline void DelEdge( const int &u, const int &v, const int &id ) {
    grph[u].erase( std :: find( grph[u].begin(), grph[u].end(), std :: make_pair( v, id ) ) ); 
    grph[v].erase( std :: find( grph[v].begin(), grph[v].end(), std :: make_pair( u, id ) ) );
}

int FindEdge( const int &u, const int &fa, const int &tar ) {
    int ret = 0;
    if( u == tar ) ret = 1e9;
    else {
        for( const auto &e : grph[u] )
            if( e.first ^ fa ) {
                int tmp = FindEdge( e.first, u, tar );
                if( tmp ) ret = std :: min( tmp, e.second );
            }
    }
    return ret;
}

int main() {
    Read( N ), Read( M );
    rep( i, 1, M ) {
        Read( edg[i].u ), Read( edg[i].v ), Read( edg[i].w );
        iniWei[i] = edg[i].w, edg[i].id = i;
    }
    Read( Q );
    rep( i, 1, Q ) Read( arg[i] );

    std :: sort( edg + 1, edg + 1 + M );
    rep( i, 1, M ) {
        int tmp = FindEdge( edg[i].u, 0, edg[i].v );
        if( tmp ) {
            DelEdge( edg[tmp].u, edg[tmp].v, tmp );
            lef[edg[i].id] = 1 + ( edg[tmp].w + edg[i].w ) / 2;
        } else lef[edg[i].id] = 0;
        AddEdge( edg[i].u, edg[i].v, i );
    }
    rep( i, 1, N ) grph[i].clear();
    per( i, M, 1 ) {
        int tmp = - FindEdge( edg[i].u, 0, edg[i].v );
        if( tmp ) {
            DelEdge( edg[tmp].u, edg[tmp].v, - tmp );
            rig[edg[i].id] = ( edg[tmp].w + edg[i].w ) / 2;
        } else rig[edg[i].id] = arg[Q] + 1;
        AddEdge( edg[i].u, edg[i].v, - i );
    }

    rep( i, 1, M ) {
        int l = std :: lower_bound( arg + 1, arg + 1 + Q, lef[i] ) - arg,
            r = std :: upper_bound( arg + 1, arg + 1 + Q, rig[i] ) - arg - 1;
        if( l <= r ) {
            int p = std :: lower_bound( arg + 1, arg + 1 + Q, iniWei[i] ) - arg;
            if( l < p ) {
                coe1[l] --, coe2[l] += iniWei[i];
                coe1[p] ++, coe2[p] -= iniWei[i];
            }
            if( p <= r ) {
                coe1[p] ++, coe2[p] -= iniWei[i];
                coe1[r + 1] --, coe2[r + 1] += iniWei[i];
            }
        }
    }
    rep( i, 1, Q ) {
        coe1[i] += coe1[i - 1];
        coe2[i] += coe2[i - 1];
        Write( arg[i] * coe1[i] + coe2[i] ), putchar( '\n' );
    }
    return 0;
}
posted @ 2023-04-24 20:20  crashed  阅读(126)  评论(0编辑  收藏  举报