經典的樹的分治,首先,對於答案是分數形式的基本上要使用二分答案,這不是胡扯,詳情請見胡伯濤的**。
那麼對於這個題,由於不好估計邊分治複雜度(其實邊分治是可以過的,好像比點分治要快),對於答案,化成如下形式sigma-ans*s=0,對於乙個可行的ans設為val,必然滿足sigma-val*s>=0,所以二分。
點分治時,將每個節點到根的路徑的權值計算出來,同時記錄走過了多少條邊。然後按照在每個子樹中按照路徑走過的邊的數目排序,二分答案後驗證,合併子樹時,第一顆子樹在乙個陣列list中按序儲存走過s條邊的路徑權值最大值,以後的子樹中列舉每條路徑,利用單調性每次從list中加入乙個值用單調佇列維護路徑的合法性和最優性,在做完一顆子樹後這個子樹儲存的路徑與list陣列儲存的資訊合併,作為下一次list使用。
複雜度分析:點分治o(nlogn),每個節點被訪問logn+1次,排序o(nlogn)為上限,二分答案驗證為o(nlogans),一共做logn次,所以為o(nlognlogans)
code:(注:不要作為對拍程式,貌似實現有bug)
#include#include#include#includestruct element
que[100005],lis[2][100005];
const double oo=10000000;
const element ori=;
int vis[100005],sta[100005],next[200005],link[200005],head[100005],sum[100005],f[100005],l[100005],r[100005],dis[100005],a[100005],b[100005],count[100005],w[200005],pre[200005],tot[2],hea=0,tai=0;
bool flag[100005];
int time=0,top=0,tail=0,now=0,e=1,up=0,dn=0,n=0;
double ans=0,limitup=0,limitdn=0,mid=0;
inline void add(int u,int v,int tmpw)
inline void update(double &a,double b)
}void print(int s)
void dfs(int s)
}inline void swap(int &a,int &b)
inline void qs(int h,int g)
}if (lup && j>0;j--);
for (;com(lis[now][j].key+b[i]) && j>0;j--)
if (hea<=tai && que[hea].val+tmpw>-(1e-5) && com(que[hea].key+b[i])) return 1;
} oth=now;
now=1-now;
tot[now]=0;
i=l[x];j=1;
lis[oth][tot[oth]+1].key=n+1;
while (i<=r[x] || j<=tot[oth])
}return 0;
}void getans(int s)
{ if (tail
WC2010 重建計畫
嘟嘟嘟 要不這篇部落格我水一下?思路很顯然,點分治 01分數規劃 單調佇列。但就是難寫。點分治的時候我們把每乙個點到重心這條鏈按深度排序,然後對於每乙個點的鏈就有乙個連續深度的區間可以和這條鏈拼上,因為要找一條權值大於 0 的鏈,那就相當於找這個區間的最大值。然後隨著點深度遞增,這個區間就不斷向左移...
WC2010 重建計畫 題解
給定一棵樹,邊有邊權,要求找到一條長度在 l,r 之間的鏈,使得鏈的總價值和除以鏈長最大 顯然,這個式子的形式讓人想到了01分數規劃。於是根據01分數規劃的套路,先二分乙個答案 考慮如何判斷,現在等於把所有邊都減去了乙個 mid 要看有沒有一條價值大於0的長度介於 l,r 的鏈 是不是感覺點分治呼之...
P4292 WC2010 重建計畫
分數規劃和長鏈剖分大體思路大佬們已經講得很清楚了 奈何我太蒟了被有個問題卡了我很久 就是怎麼分配每個子樹對應的線段樹的空間 當我們正在處理x點時 先遞迴處理完它的重兒子也就是y 然後直接讓x繼承y的資料 若y對應的陣列長這樣子 我們發現在y這棵樹中 x為距離1的點就是y距離0的點 x為距離2的點就是...