WC 2010 rebuild 重建計畫

2021-06-04 10:54:48 字數 1592 閱讀 5130

經典的樹的分治,首先,對於答案是分數形式的基本上要使用二分答案,這不是胡扯,詳情請見胡伯濤的**。

那麼對於這個題,由於不好估計邊分治複雜度(其實邊分治是可以過的,好像比點分治要快),對於答案,化成如下形式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的點就是...