CF671D Roads in Yusland

考虑 dp ,设 $dp[u]$ 表示覆盖完 $u$ 向上的边所需要的最小代价。这个怎么转移呢?

我们考虑当前 dfs 到了 $u$ 这个点,维护所有包含 $u$ 这个点的路径在钦定被选择时,覆盖完整个 $u$ 的子树所需要的最小代价。如果维护了这个,答案就是所有在 $u$ 子树内起始的路径的最小值。我们考虑用线段树维护 子树内起始的,包含这个点的最小值 这个东西。对于从这个点离开的路径,将它的这个代价设为 $\infin$ 即可。

设 $S = \sum_{v\in son[u]} dp[v]$ ,现在我们考虑怎么将一个路径从 覆盖 $v$ 的子树变成覆盖 $u$ 的子树(其中 $v$ 是 $u$ 的儿子)。明显,我们需要把覆盖 $v$ 的所有路径代价加上 $S - dp[v]$ ,相当于从其他节点选择最优情况,这个子树内钦定选择了它。

复杂度是 $O(n\log n)$ 。只需要写一个线段树区间加,区间取 $\min$ 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "cmath"
#include "vector"
#include "map"
#include "set"
#include "queue"
using namespace std;
#define MAXN 300006
//#define int long long
#define rep(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++i)
#define per(i, a, b) for (int i = (a), i##end = (b); i >= i##end; --i)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define vi vector<int>
#define all(x) (x).begin() , (x).end()
#define mem( a ) memset( a , 0 , sizeof a )
typedef long long ll;
int n , m;
const ll inf = 1e18;
vi G[MAXN];
ll dp[MAXN];
vi in[MAXN] , ot[MAXN];
int w[MAXN] , P[MAXN];
int L[MAXN] , R[MAXN] , cnt;
void afs( int u , int fa ) {
L[u] = cnt + 1;
for( auto& t : in[u] ) P[t] = ++ cnt;
for( auto& v : G[u] ) if( v != fa ) afs( v , u );
R[u] = cnt;
}

ll T[MAXN << 2] , lz[MAXN << 2];
inline void pu( int rt ) {
T[rt] = min( T[rt << 1] , T[rt << 1 | 1] );
}
inline void addit( int rt , ll c ) {
T[rt] = min( T[rt] + c , inf ) , lz[rt] += c;
}
inline void pd( int rt ) {
if( lz[rt] ) {
addit( rt << 1 , lz[rt] ) , addit( rt << 1 | 1 , lz[rt] );
lz[rt] = 0;
}
}
void add( int rt , int l , int r , int L , int R , ll c ) {
if( L <= l && R >= r ) { addit( rt , c ); return; }
pd( rt );
int m = l + r >> 1;
if( L <= m ) add( rt << 1 , l , m , L , R , c );
if( R > m ) add( rt << 1 | 1 , m + 1 , r , L , R , c );
pu( rt );
}
void mdfy( int rt , int l , int r , int p , ll c ) {
if( l == r ) { T[rt] = c; return; }
pd( rt );
int m = l + r >> 1;
if( p <= m ) mdfy( rt << 1 , l , m , p , c );
else mdfy( rt << 1 | 1 , m + 1 , r , p , c );
pu( rt );
}
ll que( int rt , int l , int r , int L , int R ) {
if( L <= l && R >= r ) return T[rt];
pd( rt );
int m = l + r >> 1; ll mn = inf;
if( L <= m ) mn = min( mn , que( rt << 1 , l , m , L , R ) );
if( R > m ) mn = min( mn , que( rt << 1 | 1 , m + 1 , r , L , R ) );
return mn;
}

void dfs( int u , int fa ) {
ll S = 0;
for( auto& v : G[u] ) if( v != fa ) {
dfs( v , u );
S = min( S + dp[v] , inf );
}
if( u == fa ) {
dp[u] = S; return;
}
for( auto& x : in[u] )
mdfy( 1 , 1 , m , P[x] , w[x] + S );
for( auto& x : ot[u] )
mdfy( 1 , 1 , m , P[x] , inf );
for( auto& v : G[u] ) if( v != fa && L[v] <= R[v] ) add( 1 , 1 , m , L[v] , R[v] , S - dp[v] );
if( L[u] <= R[u] )
dp[u] = que( 1 , 1 , m , L[u] , R[u] );
else dp[u] = inf;
}
void solve() {
cin >> n >> m;
int u , v;
rep( i , 2 , n ) scanf("%d%d",&u,&v) , G[u].pb( v ) , G[v].pb( u );
rep( i , 1 , m ) scanf("%d%d%d",&u,&v,w + i) , in[u].pb( i ) , ot[v].pb( i );
afs( 1 , 1 );
rep( i , 0 , 4 * m ) T[i] = inf;
dfs( 1 , 1 );
cout << ( dp[1] >= inf ? -1 : dp[1] ) << endl;
}

signed main() {
// int T;cin >> T;while( T-- ) solve();
solve();
}


文章作者: yijan
文章链接: https://yijan.co/cf671d-roads-in-yusland/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yijan's Blog