[SDOI2011]消耗战

题目链接

问题分析

显然是虚树题。由于\(\sum k\leqslant 500000\),所以直接套个虚树就好了。时间经过实践是可以的

参考代码

#include <bits/stdc++.h>
using namespace std;

const int Maxn = 250010;
const long long INF = 125000000010;
const int MaxLog = 20;
struct edge {
    int To, Next;
    long long c;
    edge() : To( 0 ), Next( 0 ), c( 0 ) {} 
    edge( int _To, int _Next, long long _c ) : To( _To ), Next( _Next ), c( _c ) {}
};
edge Edge[ Maxn << 1 ];
int Start[ Maxn ], UsedEdge;
inline void AddEdge( int x, int y, long long z ) {
    Edge[ ++UsedEdge ] = edge( y, Start[ x ], z );
    Start[ x ] = UsedEdge;
    return;
}
int n, Dfn[ Maxn ], Deep[ Maxn ], Time;
struct step {
    int To;
    long long Min;
    step() : To( 0 ), Min( INF ) {}
    step( int _To, long long _Min ) : To( _To ), Min( _Min ) {} 
};
step Step[ Maxn ][ MaxLog ];
int m, k, H[ Maxn ];

void Build( int u, int Fa, long long c ) {
    Dfn[ u ] = ++Time;
    Deep[ u ] = Deep[ Fa ] + 1;
    Step[ u ][ 0 ] = step( Fa, c );
    for( int i = 1; i < MaxLog; ++i )
        Step[ u ][ i ] = step( Step[ Step[ u ][ i - 1 ].To ][ i - 1 ].To, 
                min( Step[ u ][ i - 1 ].Min, Step[ Step[ u ][ i - 1 ].To ][ i - 1 ].Min ) );
    for( int t = Start[ u ]; t; t = Edge[ t ].Next ) {
        int v = Edge[ t ].To;
        if( v == Fa ) continue;
        Build( v, u, Edge[ t ].c );
    }
    return;
}

inline bool Cmp( int x, int y ) {
    return Dfn[ x ] < Dfn[ y ];
}

edge Edge2[ Maxn << 1 ];
int Start2[ Maxn ], UsedEdge2, Flag[ Maxn ], IsFlag[ Maxn ], Emmm;
int Stack[ Maxn ];
long long GetCost( int x, int y );
inline void AddEdge_2( int x, int y, long long z, int flag ) {
    if( Flag[ x ] != flag ) {
        Start2[ x ] = 0;
        Flag[ x ] = flag;
    }
    Edge2[ ++UsedEdge2 ] = edge( y, Start2[ x ], z );
    Start2[ x ] = UsedEdge2;
    return;
}
inline void AddEdge2( int x, int y, int flag ) {
    long long z = GetCost( x, y );
    AddEdge_2( x, y, z, flag );
    AddEdge_2( y, x, z, flag );
    return;
}

int GetLca( int x, int y ) {
    if( Deep[ x ] < Deep[ y ] ) swap( x, y );
    for( int i = MaxLog - 1; i >= 0; --i )
        if( Deep[ Step[ x ][ i ].To ] >= Deep[ y ] )
            x = Step[ x ][ i ].To;
    if( x == y ) return x;
    for( int i = MaxLog - 1; i >= 0; --i ) 
        if( Step[ x ][ i ].To != Step[ y ][ i ].To ) {
            x = Step[ x ][ i ].To;
            y = Step[ y ][ i ].To;
        }
    return Step[ x ][ 0 ].To;
}

long long GetCost( int x, int y ) {
    if( Deep[ x ] < Deep[ y ] ) swap( x, y );
    long long Ans = INF;
    for( int i = MaxLog - 1; i >= 0; --i ) 
        if( Deep[ Step[ x ][ i ].To ] >= Deep[ y ] ) {
            Ans = min( Ans, Step[ x ][ i ].Min );
            x = Step[ x ][ i ].To;
        }
    return Ans;
}

long long Dp( int u, int Fa ) {
    long long Ans = 0;
    int Cnt = 0;
    for( int t = Start2[ u ]; t; t = Edge2[ t ].Next ) {
        int v = Edge2[ t ].To;
        if( v == Fa ) continue;
        ++Cnt;
        if( IsFlag[ v ] == Emmm )
            Ans += Edge2[ t ].c;
        else
            Ans += min( Edge2[ t ].c, Dp( v, u ) );
    }
    if( Cnt ) return Ans; else return INF;
}

int main() {
    scanf( "%d", &n );
    for( int i = 1; i < n; ++i ) {
        int x, y; long long z;
        scanf( "%d%d%lld", &x, &y, &z );
        AddEdge( x, y, z );
        AddEdge( y, x, z );
    }
    Build( 1, 1, INF );
    scanf( "%d", &m );
    for( int i = 1; i <= m; ++i ) {
        scanf( "%d", &k );
        for( int j = 1; j <= k; ++j ) scanf( "%d", &H[ j ] );
        sort( H + 1, H + k + 1, Cmp );
        for( int j = 1; j <= k; ++j ) IsFlag[ H[ j ] ] = i;
        Emmm = i;
        UsedEdge2 = 0;
        Stack[ 0 ] = 1; Stack[ 1 ] = 1;
        for( int j = 1; j <= k; ++j ) {
            int Lca = GetLca( Stack[ Stack[ 0 ] ], H[ j ] );
            if( Deep[ Lca ] == Deep[ Stack[ Stack[ 0 ] ] ] ) {
                Stack[ ++Stack[ 0 ] ] = H[ j ];
            }
            else {
                while( Deep[ Lca ] < Deep[ Stack[ Stack[ 0 ] - 1 ] ] ) {
                    AddEdge2( Stack[ Stack[ 0 ] ], Stack[ Stack[ 0 ] - 1 ], i );
                    --Stack[ 0 ];
                }
                if( Deep[ Lca ] == Deep[ Stack[ Stack[ 0 ] - 1 ] ] ) {
                    AddEdge2( Stack[ Stack[ 0 ] ], Stack[ Stack[ 0 ] - 1 ], i );
                    --Stack[ 0 ];
                    Stack[ ++Stack[ 0 ] ] = H[ j ];
                } else {
                    AddEdge2( Stack[ Stack[ 0 ] ], Lca, i );
                    --Stack[ 0 ];
                    Stack[ ++Stack[ 0 ] ] = Lca;
                    Stack[ ++Stack[ 0 ] ] = H[ j ];
                }
            }
        }
        while( Stack[ 0 ] >= 2 ) {
            AddEdge2( Stack[ Stack[ 0 ] ], Stack[ Stack[ 0 ] - 1 ], i );
            --Stack[ 0 ];
        }
        printf( "%lld\n", Dp( 1, 0 ) );
    }
    return 0;
}

转载于:https://www.cnblogs.com/chy-2003/p/11594216.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值