题目大意
给定一棵
n
个结点的树,每个点有点权
现在选出
k
条不相交的路径,记点权和为
选取路径前需要选择一个参数
C∈[0,T]
,令
Vi=(Vi+C)%Limit
Data Constraint
n≤5000,V,T≤Limit≤105
题解
可以注意到,可能的
C
的取值最多只有
然后考虑二分,记当前二分的答案为
那么
Ans<∑ki=1(Si−Ans)
,其中
Si
是第
i
条路径经过的点的点权和。
这个就可以树形DP解决了,设
转移比较显然。
时间复杂度: O(n2logn)
这个复杂度是过不了的。所以考虑随机枚举
C
,然后通过可行解判断来优化。
这是
时间复杂度: O(nlog2n)
SRC
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std ;
#define N 5000 + 10
const double eps = 1e-7 ;
vector < int > G[N] ;
double f[N][3] , g[N] ;
int V[N] , a[N] , P[N] , C[N] ;
int n , limit , T ;
double ans , st ;
void DFS( int x , int F ) {
f[x][0] = 0 ;
g[x] = -1e15 ;
f[x][1] = a[x] - st ;
f[x][2] = a[x] - st ;
double fi = -1e15 , se = -1e15 ;
int UP = (signed) G[x].size() ;
for (int p = 0 ; p < UP ; p ++ ) {
int Son = G[x][p] ;
if ( Son == F ) continue ;
DFS( Son , x ) ;
f[x][0] += g[Son] ;
if ( f[Son][1] - g[Son] > fi ) se = fi , fi = f[Son][1] - g[Son] ;
else if ( f[Son][1] - g[Son] > se ) se = f[Son][1] - g[Son] ;
}
f[x][1] = max( f[x][1] , f[x][0] + fi + a[x] ) ;
f[x][2] = max( f[x][2] , f[x][0] + fi + se + a[x] + st ) ;
g[x] = max( f[x][0] , max( f[x][1] , f[x][2] ) ) ;
}
inline bool Check( double m ) {
st = m ;
DFS( 1 , 0 ) ;
return g[1] - m > 0 ;
}
int main() {
srand( 1007 ) ;
scanf( "%d%d" , &n , &limit ) ;
for (int i = 1 ; i <= n ; i ++ ) scanf( "%d" , &V[i] ) ;
for (int i = 1 ; i < n ; i ++ ) {
int x , y ;
scanf( "%d%d" , &x , &y ) ;
G[x].push_back(y) ;
G[y].push_back(x) ;
}
scanf( "%d" , &T ) ;
for (int i = 1 ; i <= n ; i ++ ) C[i] = min( limit - V[i] - 1 , T ) ;
sort( C + 1 , C + n + 1 ) ;
for (int i = 1 ; i <= n ; i ++ ) {
if ( i > 1 && C[i] == C[i-1] ) continue ;
C[++C[0]] = C[i] ;
}
for (int i = 1 ; i <= C[0] ; i ++ ) P[i] = i ;
random_shuffle( P + 1 , P + C[0] + 1 ) ;
for (int i = 1 ; i <= min(C[0],4000) ; i ++ ) {
int now = P[i] ;
double l = 0 , r = 0 ;
for (int j = 1 ; j <= n ; j ++ ) {
a[j] = (V[j] + C[now]) % limit ;
r += a[j] ;
}
if ( !Check(ans) ) continue ;
while ( l + eps <= r ) {
double mid = (l + r) / 2 ;
if ( Check( mid ) ) l = mid + eps , ans = max( ans , mid ) ;
else r = mid - eps ;
}
}
printf( "%.6lf\n" , ans ) ;
return 0 ;
}
以上.