期末考完上競賽,新春佳節上競賽,元宵大年上競賽,開學繼續上競賽
╰(‵□′)╯
# 題面
給定一棵 \(n\) 個點的邊帶權的樹,要求選出一條邊的數量在 \(L,R\) 之間的簡單路徑,使得該路徑的邊權平均值最大,求該最大平均值,
資料規模:\(n\le10^5\),邊權不超過 \(10^6\),
# 決議
平均值問題,不難想到分數規劃,于是首先二分出答案,然后給每個邊權減去二分值,問題轉化為「找出一條邊的數量在 \(L,R\) 之間,邊權和大于等于 \(0\) 的簡單路徑」,這樣的話,其實就相當于求邊權和最大的路徑,判斷其邊權是否大于等于 \(0\),
樹上的路徑問題,于是可以想到樹分治,不能邊分治,因為邊分治需要改變樹形,重構出的樹的路徑的邊權平均值和原樹是不一樣的,只能考慮點分治,
那么就是要統計過重心的路徑,考慮依次(可以先想一下依什么次)遍歷子樹,統計出已經遍歷的子樹中「到重心邊數為 \(i\) 的路徑的最大權」記為 \(f_i\),為了避免算到兩個端點在同一棵子樹中的路徑(非簡單路徑),我們要先用計入子樹 \(v\) 之前的 \(f_i\) 與子樹 \(v\) 來計算答案后,再將子樹 \(v\) 合并到 \(f_i\) 中,
怎么計算答案?我們按路徑長度從小到大列舉子樹 \(v\) 的一條路徑,不妨記該路徑長度為 \(i\),那么另一條路徑長度范圍 \([L-i,R-i]\),隨著 \(i\) 增大,這個區間是只會往左移動的,而我們要求的就是這個區間中 \(f_i\) 的最大值,這個問題可以用單調佇列解決,
現在來分析一下時間復雜度,記當前分治的連通塊大小為 \(S\),\(f_i\) 的長度是 \(O(S)\) 的,那么每求一次答案需要將 \(f_i\) 掃一遍,所以復雜度是 \(O(S^2)\) 的???這與點分治要求的每個連通塊內的復雜度 \(O(S)\) 差得也太多了,
不難想到,在將子樹 \(v\) 的資訊合并到 \(f_i\) 的程序中,\(f_i\) 的長度是不斷增大的,所以 \(f_i\) 掃描的起點不從 \(S\) 開始,而是從當前有值的位置開始,
但是這樣仍然不足夠——如果我們遍歷的第一棵子樹是所有子樹中最大的一棵(最大有 \(\frac{S}{2}\),因為是點分治嘛)那么合并資訊后 \(f_i\) 的長度直接就變成了 \(O(S)\),之后每棵子樹都要掃一遍 \(f_i\),復雜度還是 \(O(S^2)\) 的,
所以注意到之前的那個問題「按什么順序遍歷子樹?」我們可以聰明一點,先從深度小的開始遍歷,類似于啟發式的思想(不知道算不算啟發式),再分析復雜度,在計算某一棵子樹的答案時,\(f_i\) 的長度是上一棵子樹的深度,總的遍歷的復雜度是所有子樹深度之和,于是就保證了復雜度是 \(O(S)\) 的,
于是最終復雜度 \(O(n\log n\log w)\)(\(w\) 是二分的權值范圍),
# 源代碼
點擊展開/折疊代碼
/*Lucky_Glass*/
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int rin(int &r){
int b=1,c=getchar();r=0;
while(c<'0' || '9'<c) b=c=='-'?-1:b,c=getchar();
while('0'<=c && c<='9') r=(r<<1)+(r<<3)+(c^'0'),c=getchar();
return r*=b;
}
const int N=1e5+10;
#define con(type) const type &
const double EPS=1e-8;
typedef pair<int,int> pii;
struct SuperDeque{
int ary[N],le,ri;
void clear(){le=1,ri=0;}
void push_back(con(int)x){ary[++ri]=x;}
void pop_back(){ri--;}
void pop_front(){le++;}
int back(){return ary[ri];}
int front(){return ary[le];}
int size(){return ri-le+1;}
}que;
int n,bonl,bonr,rsiz,rwei,rroot;
int siz[N],hgt[N];
double val_bas;
double val_bin[N],tmp_bin[N];
bool ban[N],ifsort[N];
vector<pii> lnk[N];
int findCenter(con(int)u,con(int)fa){
int maxsizv=0,sizu=1;
for(int it=0,itt=(int)lnk[u].size();it<itt;it++){
int v=lnk[u][it].first;
if(v==fa || ban[v]) continue;
int sizv=findCenter(v,u);
maxsizv=max(maxsizv,sizv);
sizu+=sizv;
}
int weiu=max(rsiz-sizu,maxsizv);
if(weiu<rwei) rwei=weiu,rroot=u;
return sizu;
}
int dataDFS(con(int)u,con(int)fa,con(double)val,con(int)dep){
int ret=0;
siz[u]=1;
tmp_bin[dep]=max(tmp_bin[dep],val);
for(int it=0,itt=(int)lnk[u].size();it<itt;it++){
int v=lnk[u][it].first;
if(v==fa || ban[v]) continue;
ret=max(ret,dataDFS(v,u,val+lnk[u][it].second-val_bas,dep+1));
siz[u]+=siz[v];
}
return ret+1;
}
int highDFS(con(int)u,con(int)fa){
int ret=0;
for(int it=0,itt=(int)lnk[u].size();it<itt;it++){
int v=lnk[u][it].first;
if(v==fa || ban[v]) continue;
ret=max(ret,highDFS(v,u));
}
return ret+1;
}
bool cmp(con(pii)a,con(pii)b){return hgt[a.first]<hgt[b.first];}
bool ina_abs(con(double)key){return key<0?-key:key;}
int sgn(con(double)key){return ina_abs(key)<EPS?0:(key<0?-1:1);}
bool dacDFS(int u,con(int)totsiz){
ban[u]=true;
if(!ifsort[u]){
for(int it=0,itt=(int)lnk[u].size();it<itt;it++){
int v=lnk[u][it].first;
if(ban[v]) continue;
hgt[v]=highDFS(v,u);
}
sort(lnk[u].begin(),lnk[u].end(),cmp);
ifsort[u]=true;
}
fill(val_bin,val_bin+totsiz+1,-1e9),val_bin[0]=0;
int ardlen=0;
for(int it=0,itt=(int)lnk[u].size();it<itt;it++){
int v=lnk[u][it].first;
if(ban[v]) continue;
int hgtv=dataDFS(v,0,lnk[u][it].second-val_bas,1);
que.clear();
for(int i=1,j=ardlen;i<=hgtv;i++){
while(j>=bonl-i && j>=0){
while(que.size() && sgn(val_bin[que.back()]-val_bin[j])<=0)
que.pop_back();
que.push_back(j);
j--;
}
while(que.size() && que.front()>bonr-i) que.pop_front();
if(que.size() && sgn(val_bin[que.front()]+tmp_bin[i])>=0)
return true;
}
for(int i=1;i<=hgtv;i++){
val_bin[i]=max(val_bin[i],tmp_bin[i]);
tmp_bin[i]=-1e9;
}
ardlen=hgtv;
}
for(int it=0,itt=(int)lnk[u].size();it<itt;it++){
int v=lnk[u][it].first;
if(ban[v]) continue;
rwei=1e9,rsiz=siz[v];
findCenter(v,0);
if(dacDFS(rroot,siz[v])) return true;
}
return false;
}
bool check(con(double)mmi){
fill(tmp_bin,tmp_bin+1+n,-1e9);
for(int i=1;i<=n;i++) ban[i]=false;
val_bas=mmi;
rsiz=n,rwei=1e9,findCenter(1,0);
return dacDFS(rroot,n);
}
int main(){
rin(n),rin(bonl),rin(bonr);
for(int i=1,u,v,l;i<n;i++){
rin(u),rin(v),rin(l);
lnk[u].emplace_back(v,l);
lnk[v].emplace_back(u,l);
}
double lle=0,rri=1e6,mmi;
while(lle+1e-4<rri){
mmi=(lle+rri)/2;
if(check(mmi)) lle=val_bas;
else rri=val_bas;
}
printf("%.3f\n",lle);
return 0;
}
THE END
Thanks for reading!
進退之間覺醒 逆轉之力
被黑暗俘虜 想讓我沉溺
絕不會認輸 信己不信命
崩壞的秩序 打破既定的結局
——《陷落之序》By 著小生zoki
> Link 陷落之序-Bilibili
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/264108.html
標籤:其他
上一篇:Ch1-What is DAX?
