BJOI2017 樹的難題

2022-03-01 20:10:20 字數 1702 閱讀 1438

按照常規思路,選乙個點x作為分治中心,拼接x出發到子樹各點的路徑。對於拼接時兩段介面處(即x連出的那條邊,若沒有,設為0號邊:顏色為0,長度為0,到達0號兒子)顏色的影響,可以記錄每段的路徑權值、邊數以及該段的介面,將所有的路徑以介面顏色為第一關鍵字,介面編號為第二關鍵字排序。顯然,對於同一介面的路徑必為連續的一段序列。這樣列舉每個路徑,找到之前出現的符合邊數和的要求的最大的路徑權值即可。用兩顆線段樹維護,一棵維護與本路徑不同顏色的,一顆維護與本路徑顏色相同但介面不同的(因為簡單路徑的拼接要滿足兩個端點不在同乙個子樹內)。

時間複雜度 o(nlognlogn)

常數巨大,需要吸氧和c++11

#include using namespace std;

const int n=2e5+10;

const int m=4e5+10;

const int inf=0x3f3f3f3f;

int n,m,l,r;

int c[n],head[n],to[m],clr[m],last[m];

int sum,top,rt,fiz[n],siz[n];

bool ban[n];

struct seg p[n];

struct rmq

void pushdown(int x)

void modify(int x,int l,int r,int p,int w)

int query(int x,int l,int r,int l,int r)

inline void modify(int p,int w)

inline int query(int l,int r)

#undef ls

#undef rs

} a,b;

void addedge(int x,int y,int c)

void getroot(int x,int pa)

fiz[x]=max(fiz[x],sum-siz[x]);

if(fiz[x]} int ans=-2e9;

void getdis(int x,int pa,int dis,int num,int pclr,int bel) ;

for(int i=head[x]; i; i=last[i])

}void calc(int x) ;

for(int i=head[x]; i; i=last[i])

sort(p+1,p+top+1,[=](seg x,seg y));

a.clear();

b.clear();

for(int l=1,r; l<=top; l=r+1)

b.clear();

if(l!=1) for(int i=l; i<=r; ++i)

ans=max(ans,p[i].dis+a.query(l-p[i].num,r-p[i].num));

for(int i=l; i<=r; ++i) a.modify(p[i].num,p[i].dis); }}

void solveat(int x)

}int main()

rt=0;

sum=n;

fiz[0]=2e9;

getroot(1,0);

solveat(rt);

printf("%d\n",ans);

return 0;

}

BJOI2017 樹的難題 點分治 線段樹

傳送門 傳送門 題意 給一棵樹,樹上有顏色,每種顏色有權值,定義一條路徑的權值為所有顏色相同段的權值之和,求長度在 l,r l,r l,r 中的路徑的最大權值。資料範圍 暴力過不了 顯然是個點分治 對於分治中心考慮過中心的路徑貢獻的答案 以下的 子樹 指根直接與分治中心相連的子樹 把分治中心作為根,...

BJOI2017 樹的難題 點分治,線段樹合併

lg傳送門 點分治 線段樹合併。我不會寫單調佇列,所以就寫了好寫的線段樹。考慮對於每乙個分治中心,把出邊按顏色排序,這樣就能把顏色相同的子樹放在一起處理。用一棵動態開點線段樹維護顏色不同的子樹的資訊,另一棵動態開點線段樹維護顏色相同的子樹的資訊,同時按照題目要求更新答案。當子樹顏色變化時,就把第二棵...

BJOI2017 魔法咒語

矩陣乘法 ac 自動機 是道很不錯的題了 首先是前六十分,就是乙個 ac 自動機上的套路 dp 設 dp i j 表示匹配出的長度為 i 在自動機上位置為 j 的方案數,轉移的話就列舉下乙個單詞選擇哪個放到自動機上一波匹配就好了 後面 40 分強行變成了另外一道題,l 變成了 1e8 一看就是矩乘的...