WC2010 重建計畫 長鏈剖分 點分治

2022-03-31 17:17:56 字數 3350 閱讀 9646

題目描述

有一棵大小為 \(n\) 的樹,給定 \(l, r\) ,要求找到一條長度在 \([l, r]\) 的路徑,並且路徑上邊權的平均值最大

\(1 \leq n,l,r \leq 10^5\)

前幾天沉迷初賽來寫幾道資料結構恢復一下**能力,坑填完之後可能就要開始啃思維題了qwq。

這個題貌似長鏈剖分和點分複雜度都是 \(o(nlog^2n)\) 的,點分好久都沒碰了,長鏈剖分也只有暑假裡口胡了幾個多校的題而已,先講做法吧

這個題很顯然可以分數規劃,二分答案後問題轉化為每條邊邊權變為\(w_i - mid\) ,判斷能不能找到一條長度在 \([l, r]\) 且邊權和非負的路徑,實際上可以求一條滿足條件且邊權和最大的路。

點分治:

比較顯然的做法是對於每乙個分治中心用乙個資料結構來維護到點分中心的每一種長度的路徑的最值,然後暴力拼合路徑即可,這樣做的話總複雜度是 \(o(nlog^3n)\) ,感覺不太能松的過去。

簡單觀察發現,對於當前長度 \(i\) ,隨著 \(i\) 遞增可行的區間是單調左移的,所以可以用乙個單調佇列來處理每乙個分治中心的答案。不過要注意的是,總的區間大小等價於當前處理到的最長的路徑長度,如果先處理一條很長的路徑,剩下的點數很多的話複雜度就退化到 \(o(n^2logn)\) 了。

解決方法是對每乙個兒子按照其最長路徑長度排序(也就是最大深度),這樣處理每乙個兒子的複雜度不會超過其最長路徑長度,複雜度就是正確的 \(o(nlog^2n)\) 了,類似的套路也可以在 \(\text\) 見到

/*program by mangoyang*/

#include#define inf (0x7f7f7f7f)

#define max(a, b) ((a) > (b) ? (a) : (b))

#define min(a, b) ((a) < (b) ? (a) : (b))

typedef long long ll;

using namespace std;

template inline void read(t &x)

const int n = 200005;

const double eps = 1e-4;

int a[n], b[n], nxt[n], head[n], cnt, n, l, r;

inline void add(int x, int y, int z)

namespace tree

msize = max(msize, all - sz[u]);

if(msize <= mn) mn = msize, root = u;

}inline void build(int u)

}inline void realmain()

inline bool cmp(int a, int b)

inline void getdis(int u, int fa, int x, int dep, double dis)

}inline void solve(int u)

sort(now.begin(), now.end(), cmp);

for(int i = 1; i <= len; i++) f[i] = -inf;

int mx = 0;

for(int pos = 0; pos < now.size(); pos++)

if(h <= t) res = max(res, c[x][i] + f[q[h]]);

}for(int i = 0; i < c[x].size(); i++)

f[i+1] = max(f[i+1], c[x][i]);

mx = c[x].size();

}for(int i = 0; i < g[u].size(); i++) solve(g[u][i]);

}inline bool check(double x)

}int main()

tree::realmain();

double l = 0, r = 1000000, ans = 0;

while(l + eps < r)

printf("%.3lf", ans);

return 0;

}

長鏈剖分

本質上點分做的事情是每次合併多個以深度為下標的陣列求最值,觀察發現這個題可以長鏈剖分。每一次保留重鏈的向上路徑,並把輕鏈的資訊一一合併上去。

但是這裡就不能用單調佇列優化了,因為重鏈是一開始就要被保留的,如果用單調佇列的話複雜度會被同樣的東西卡掉,所以必須要用線段樹來維護答案。

/*program by mangoyang*/

#include#define inf (0x7f7f7f7f)

#define max(a, b) ((a) > (b) ? (a) : (b))

#define min(a, b) ((a) < (b) ? (a) : (b))

typedef long long ll;

using namespace std;

template inline void read(t &x)

const int n = 200005;

const double eps = 1e-4;

double tag[n], f[n], ans, mid;

int a[n], b[n], nxt[n], head[n], cnt;

int dfn[n], dep[n], ms[n], w[n], tot, n, l, r;

inline void add(int x, int y, int z)

struct segmenttree

inline void modify(int u, int l, int r, int pos, double x)

inline double query(int u, int l, int r, int l, int r)

}seg;

inline void dfs(int u, int fa)

}inline void split(int u, int fa)

for(int p = head[u]; p; p = nxt[p])

for(int i = 0; i <= dep[v]; i++)

}}inline bool check()

int main()

dfs(1, 0);

double l = 0, r = 1000000, realans = 0;

while(l + eps < r)

printf("%.3lf", realans);

return 0;

}

WC2010 重建計畫 長鏈剖分

lg傳送門 又一道長鏈剖分好題。這題寫點分治的人應該比較多吧,但是我太菜了,只會長鏈剖分。如果你還不會長鏈剖分的基本操作,可以看看我的長鏈剖分總結。首先一看求平均值最大,馬上想到套個二分,每次把邊權變為原來的邊權減去二分的答案,看樹上有沒有長度在 l 和 u 之間的正權鏈就好了。於是乎問題就轉變成了...

WC2010 重建計畫

嘟嘟嘟 要不這篇部落格我水一下?思路很顯然,點分治 01分數規劃 單調佇列。但就是難寫。點分治的時候我們把每乙個點到重心這條鏈按深度排序,然後對於每乙個點的鏈就有乙個連續深度的區間可以和這條鏈拼上,因為要找一條權值大於 0 的鏈,那就相當於找這個區間的最大值。然後隨著點深度遞增,這個區間就不斷向左移...

WC2010 重建計畫 題解

給定一棵樹,邊有邊權,要求找到一條長度在 l,r 之間的鏈,使得鏈的總價值和除以鏈長最大 顯然,這個式子的形式讓人想到了01分數規劃。於是根據01分數規劃的套路,先二分乙個答案 考慮如何判斷,現在等於把所有邊都減去了乙個 mid 要看有沒有一條價值大於0的長度介於 l,r 的鏈 是不是感覺點分治呼之...