傳送門
參考部落格
所有邊減去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行,每行乙個整數...