日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[SPOJ - FTOUR2] Free tour II(点分治 + 背包dp + 启发式合并)

發布時間:2023/12/3 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [SPOJ - FTOUR2] Free tour II(点分治 + 背包dp + 启发式合并) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

SPOJ - FTOUR2 Free tour II

problem

給定一棵樹,以及 mmm 個擁擠城市編號,選擇一條最多包含 kkk 個擁擠城市的簡單路徑。

每條邊有一個有趣度 www,可正可負。簡單路徑的價值定義為包含邊的有趣度之和。

求最大價值。n≤2e5,∣w∣≤1e4n\le 2e5,|w|\le 1e4n2e5,w1e4

solution

取點作根,計算過根的符合條件的簡單路徑價值,如果不過根就轉化成各個獨立子樹的子問題。

選取重心作根,點分治。

問題仍然在于怎么計算過根的符合條件的簡單路徑價值。

暴力的樹形 dpdpdpdpi,j:idp_{i,j}:idpi,j?:i 子樹內擁擠城市為 jjj 的最大路徑價值。

每次枚舉 iii 的兒子,每次做樹上背包,之后再合并,時間復雜度是平方級別的。

dpu,x+dpv,y(x+y≤k)→ansdp_{u,x}+dp_{v,y}(x+y\le k)\rightarrow ansdpu,x?+dpv,y?(x+yk)ans

dpu,x=max?(dpu,x,dpv,x)dp_{u,x}=\max(dp_{u,x},dp_{v,x})dpu,x?=max(dpu,x?,dpv,x?)

考慮優化。

對于第 iii 個兒子而言,如果第 iii 個兒子使用的擁擠城市數量為 jjj,那么前 i?1i-1i?1 個兒子的城市擁擠數量可以使用 1~k?j1\sim k-j1k?j

所以可以“前綴和”優化,這里去前綴最大值

dpu,x:dp_{u,x}:dpu,x?:i?1i-1i?1 個兒子城市擁擠數量使用不超過 xxx 的最大價值。

這樣就可以線性枚舉 jjj 計算貢獻。

最后就是在枚舉前 i?1i-1i?1 個兒子的擁擠數量問題上。

比如第 ppp 個兒子內部最多可以找到一條路徑有 kkk 個擁擠城市,而現在的兒子最多只能有 y(y<k)y(y<k)y(y<k) 個。

但是從第 ppp 個兒子開始就必須枚舉完使用 kkk 個擁擠城市,才能將前面兒子的信息覆蓋完全。

但是這樣的時間復雜度就會飛起。

如果交換現在的兒子和第 ppp 個兒子順序,那么只用枚舉完 yyy 個擁擠城市就已經覆蓋了前面兒子的所有路徑使用情況。

所以將兒子按照內部最多能找到一條路徑有 ddd 個擁擠城市,ddd 升序排序。

相當于只將每個點枚舉了一次,這就是啟發式合并。

code

#include <bits/stdc++.h> using namespace std; #define maxn 200005 #define inf 0x7f7f7f7f vector < pair < int, int > > G[maxn]; int n, m, k, Max, root, N, ans; bool crowd[maxn], vis[maxn]; int siz[maxn], f[maxn], g[maxn]; struct node { int d, v, w; }MS[maxn];void dfs( int u, int fa ) {int maxsiz = 0; siz[u] = 1;for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i].first;if( vis[v] or v == fa ) continue;else dfs( v, u ), siz[u] += siz[v];maxsiz = max( maxsiz, siz[v] );}maxsiz = max( maxsiz, N - siz[u] );if( maxsiz < Max ) Max = maxsiz, root = u; }int dfs( int u, int fa, int cnt ) {if( cnt == k ) return cnt;int ret = cnt;for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i].first;if( vis[v] or v == fa ) continue;ret = max( ret, dfs( v, u, cnt + crowd[v] ) );}return ret; }void dfs( int u, int fa, int val, int cnt ) {if( cnt > k ) return;g[cnt] = max( g[cnt], val );for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i].first, w = G[u][i].second;if( vis[v] or v == fa ) continue;dfs( v, u, val + w, cnt + crowd[v] );} }void calc( int u ) {if( crowd[u] ) k --;int cnt = 0;for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i].first, w = G[u][i].second;if( vis[v] ) continue;MS[++ cnt] = { dfs( v, u, crowd[v] ), v, w };}//計算出v子樹內一條路最多能有多少個擁擠城市sort( MS + 1, MS + cnt + 1, []( node x, node y ) { return x.d < y.d; } );//啟發式合并for( int i = 1;i <= cnt;i ++ ) {int v = MS[i].v, w = MS[i].w;dfs( v, u, w, crowd[v] ); //計算出v子樹內訪問x個擁擠城市的最大有趣度/*f[j]:前i-1個子樹信息總和 訪問j個擁擠城市的最大值g[j]:只針對第i個子樹 訪問j個擁擠城市的最大值 每次都會在dfn(v,u,w,crowd[v])重新計算顯然 g[j]+f[x](0<=x<=k-j) 都可以對最終答案進行貢獻這里對f[x]進行前綴max 有jx平方的時間變成j線性實際上x<=MS[i-1].d 要注意這個限制 不然可能會莫須有地更新到不存在的擁擠城市個數 影響f然后將i子樹信息合并到i-1子樹信息內 即g->f轉到下一個子樹i+1*/if( i ^ 1 ) {for( int j = 1;j <= MS[i - 1].d;j ++ ) f[j] = max( f[j], f[j - 1] );for( int j = 0;j <= MS[i].d;j ++ ) ans = max( ans, f[min( MS[i - 1].d, k - j )] + g[j] );}for( int j = 0;j <= MS[i].d;j ++ ) f[j] = max( f[j], g[j] ), g[j] = 0;}for( int i = 0;i <= MS[cnt].d;i ++ ) {ans = max( ans, f[i] );f[i] = g[i] = 0;}if( crowd[u] ) k ++; }void dfs( int u ) {vis[u] = 1;calc( u );for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i].first;if( vis[v] ) continue;Max = inf, N = siz[v];dfs( v, u );dfs( root );} }int main() {scanf( "%d %d %d", &n, &k, &m );for( int i = 1, x;i <= m;i ++ ) scanf( "%d", &x ), crowd[x] = 1;for( int i = 1, u, v, w;i < n;i ++ ) {scanf( "%d %d %d", &u, &v, &w );G[u].push_back( { v, w } );G[v].push_back( { u, w } );}Max = inf, N = n;dfs( 1, 0 );dfs( root );printf( "%d\n", ans );return 0; }

總結

以上是生活随笔為你收集整理的[SPOJ - FTOUR2] Free tour II(点分治 + 背包dp + 启发式合并)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。