bzoj 2870 最長道路tree

2022-03-26 22:32:53 字數 1416 閱讀 5830

題目

邊分治邊分和點分相比就是找到一條重心邊,考慮所有經過這條邊的路徑,之後斷開這條邊分成兩個聯通塊,繼續分治

由於每次分治重心是一條邊,所以只會產生兩個聯通塊,考慮兩個聯通塊顯然要比像點分那樣考慮多個聯通塊容易

但是邊分有乙個問題,就是遇到菊花圖就自閉了,複雜度變成了\(o(n^2)\)

我們注意到邊分的複雜度還和每個點的度數有關係,於是我們建一些虛點和虛邊,把這棵樹變成一棵二叉樹,這樣複雜度就是\(o(n\operatorname)\)了

具體做法就是,一旦發現乙個節點有多與兩個兒子,就新建兩個虛點,之後把這些兒子按照奇偶性分給兩個兒子

對於這道題,虛點的點設成其父親的點權,虛邊的邊權為\(0\),我們每次把兩個聯通塊的內的路徑拿出排序,雙指標掃一掃拼一拼就好了

**

#include#define re register

#define ll long long

#define max(a,b) ((a)>(b)?(a):(b))

#define min(a,b) ((a)<(b)?(a):(b))

inline int read()

const int maxn=2e5+5;

struct ee[maxn<<1];

int head[maxn],n,num,sum[maxn],vis[maxn];

int rn,s,top[2],mnow,tax[maxn],rt,val[maxn];

struct segst[2][maxn];

std::vectorson[maxn];ll ans;

inline int cmp(const seg &a,const seg &b)

}void solve(int x,int sz)

tax[top[0]]=st[0][top[0]].l;j=1;

for(re int i=top[0]-1;i;--i) tax[i]=max(st[0][i].l,tax[i+1]);

for(re int i=1;i<=top[1];i++)

int ss=sz-sum[e[now].v];

solve(e[now].v,sum[e[now].v]);solve(e[now^1].v,ss);

}int main()

if(sz==2)

int t1=++n,t2=++n;val[t1]=val[t2]=val[i];

add(i,t1,0),add(t1,i,0);add(i,t2,0),add(t2,i,0);

for(re int j=0;jif(j&1) son[t1].push_back(son[i][j]);

else son[t2].push_back(son[i][j]);

} solve(1,n);

std::cout<}

BZOJ2870 最長道路

許可權題 給出一棵樹,點有點權,找到樹上的一條路徑使得路徑上點的個數和其中點權最小的點的點權之積最大,輸出最大值。邊分治板子題啦。邊分治後對於分出來的兩棵子樹 按到左右根的最小點權排序後直接用單調指針對每乙個點找到另一棵樹中的最優點即可。code include using namespace st...

BZOJ2870 最長道路tree

題解 子樹分治的做法可以戳這裡 可是碼量。這裡介紹另一種好寫又快的方法。我們還是一顆一顆子樹處理,處理完乙個子樹,考慮列舉最小值。如果我們現在處理到了x節點,它到根的min為w。那麼我們就可以在以前的資訊中找出min w且長度最長的一條鏈並且用它和該鏈合併,同時更新答案。這個顯然可以用樹狀陣列搞。處...

BZOJ2870 最長道路Tree(邊分治)

傳送門 邊分治比點分治好的一點就是每次分出來都只有2個集合 如果遇到難以合併的資訊的時候邊分治就很有用了 手動轉二叉樹 每次可以按照權值大小排序後對兩個集合雙指標掃兩遍 具體實現可以看 複雜度o n log2 n o nlog 2n o nlog 2n include using namespace...