點分治 二分 單調佇列 wc2010重建計畫

2021-09-30 19:59:42 字數 2489 閱讀 1361

傳送門

參考部落格

所有邊減去mid,看樹上有沒有邊權和大於等於0的路徑。可以用點分治。

列舉當前分治子樹內的結點u,那麼我們就要找到之前深度範圍為[l-dep[u],r-dep[u]]的最大值。(到當前重心的邊權和)

如果用線段樹維護,查詢和更新都要套乙個log,時間要炸。

我們發現如果dep是單增的,那麼就相當於是乙個滑動視窗問題。可以用乙個單調佇列維護。

我們用深度記錄這個單調佇列。我們只要保證深度單調,距離單減就好,因為要維護距離的最大值。用深度判斷是否符合[l,u]的長度要求。其實就是判斷佇列內元素是否在視窗內。

如果我們列舉的深度單增,那麼另一邊的深度單減,先保證下限l,再保證上限u。反之先保證r,再保證l。

然而每次滑動視窗的複雜度是由  max(之前的最大深度,當前子樹大小)  決定的。點分治只能保證子樹的大小,不能保證深度。於是網上大部分題解可以用乙個掃把圖卡成n²log,233。

然而我們只要先把子樹按照最大深度排個序就好了。

具體實現的時候,就是先從當前重心dfs,得到每個子樹的最大深度以及每種深度的最大權值。

用f[i]表示之前已經搜過的子樹中,深度為i的最大邊權和。

用g[i]表示現在正在列舉的子樹中,深度為i的最大邊權和。

用now_mxdp[u]表示u子樹中最大深度,用predep表示之前已經搜尋過的子樹中可以達到的最大深度。

然後二分mid,在f和g陣列中暴力把(mid*深度)減去。

滑動視窗做完之後記得把f陣列和g陣列給加回去。然後還要更新f陣列。

每搜完乙個子樹需要把g陣列給重置。每搜完乙個重心需要把f陣列給重置。

大概就是這樣。最後,注意精度。

二分寫點分裡面比較好,因為可以不停更新二分左邊界。

#includeusing namespace std;

const int maxn=1e5+10;

const double inf=1e9;

const double eps=1e-9;

int head[maxn],next[maxn<<1],v[maxn<<1],w[maxn<<1],cnt=0;

int dep[maxn],siz[maxn],maxsub[maxn],vis[maxn],tot=0;

int now_mxdp[maxn],to[maxn];

int n,l,u,root=0,sz,a,b,c;

int nowdep,predep,head,tail;

double dis[maxn],up=-inf,f[maxn],g[maxn],ans=inf;

inline int read()

inline void add(int u,int v,int w)

inline void getroot(int u,int f)

maxsub[u]=max(maxsub[u],sz-siz[u]);

if(maxsub[root]>maxsub[u]) root=u;

}void get_dis(int u,int f,int dep,double val)

void get_g(int u,int f,int dep)

bool check(double m,bool ret=0)

for(int i=1;i<=nowdep;++i) g[i]+=(double)i*m;

for(int i=1;i<=predep;++i) f[i]+=(double)i*m;

return ret;

}bool cmp(const int &a,const int &b)

ans=r,predep=max(predep,nowdep);

for(int i=1;i<=nowdep;++i) f[i]=max(f[i],g[i]),g[i]=-inf;

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

for(int i=head[u];i;i=next[i]) if(!vis[v[i]])

root=0,sz=siz[v[i]],getroot(v[i],u),solve(root);

}int main()

inline void add(int u,int v,int w)

inline void getroot(int u,int f)

maxsub[u]=max(maxsub[u],sz-siz[u]);

if(maxsub[root]>maxsub[u]) root=u;

}void get_dis(int u,int f,int dep,double val,double m)

void get_g(int u,int f,int dep)

bool check()

return false;

}bool cmp(const int &a,const int &b)

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

}

巡遊tour 二分 樹分治 單調佇列

題解 二分 樹分治 單調佇列 題目大意就是要求一條路徑使得其中位數最大。首先由於答案很難直接確定,採用二分 首先二分出可能答案mid。有了mid值之後對於每條邊w i 若w i mid,令w i 1,else w i 1 這樣一來,問題被轉化為樹上是否存在一條長度在 l,r 之間的非負路徑,暴力判斷...

尋找段落(二分 單調佇列)

題目描述 給定乙個長度為n的序列a i,定義a i 為第i個元素的價值。現在需要找出序列中最有價值的 段落 段落的定義是長度在 s,t 之間的連續序列。最有價值段落是指平均值最大的段落,段落的平均值 段落總價值 段落長度。輸入輸出格式 輸入格式 第一行乙個整數n,表示序列長度。第二行兩個整數s和t,...

中間數(二分) 單調佇列

1 中間數 mid.cpp c pas 問題描述 有n個整數 a1 an 任意兩個整數做絕對值差 ai aj 1 i 可以得到 n n 1 2 個絕對值差,令 m n n 1 2 保證m 一定是個偶數,求第 m 2小的數 輸入格式 輸入檔名為mid.in。第一行,乙個整數n 接下來n行,每行乙個整數...