洛谷P3384 模板 樹鏈剖分

2022-05-19 10:10:50 字數 1758 閱讀 2404

題目大意:已知一棵包含n個結點的樹(連通且無環),每個節點上包含乙個數值,需要支援以下操作:

操作1: 格式: 1 x y z 表示將樹從x到y結點最短路徑上所有節點的值都加上z

操作2: 格式: 2 x y 表示求樹從x到y結點最短路徑上所有節點的值之和

操作3: 格式: 3 x z 表示將以x為根節點的子樹內所有節點值都加上z

操作4: 格式: 4 x 表示求以x為根節點的子樹內所有節點值之和

解題思路:樹鏈剖分。

剖完進行dfs遍歷,並記錄每個節點的dfs序(優先遍歷重鏈)。

可以發現任何一條重鏈的dfs序都是連續的,並且任何一棵子樹中所有節點的dfs序也是連續的。

我們用線段樹來維護每個dfs序對應的節點的資訊。

對於操作3和4,由於一棵子樹中所有節點dfs序有序,直接修改或查詢即可。若一棵子樹的根節點的dfs序是x,子樹大小是sz,那子樹最大的乙個dfs序是x+sz-1。

由於樹剖重鏈和輕鏈數量都是log級的,加上線段樹時間複雜度,總時間複雜度$o(m\log^2 n)$。

c++ code:

#include#include#include#define ll long long

#define n 120500

int n,m,rt,p,a[n],head[n],cnt,sz[n],fa[n],dep[n],son[n],dfn[n],idx;

int aa[n],l,r,c,top[n];

ll ans;

struct segmenttreenoded[n<<2];

struct edgee[n<<1];

inline int readint()

void dfs(int now)

}void dfs2(int now)

void build(int l,int r,int o);

return;

} int mid=(l+r)>>1;

build(l,mid,o<<1);

build(mid+1,r,o<<1|1);

d[o].sum=(d[o<<1].sum+d[o<<1|1].sum)%p;

d[o].add=0;

}void add_t(int l,int r,int o)

int mid=(l+r)>>1;

update(r-l+1,o);

if(l<=mid)add_t(l,mid,o<<1);

if(mid>1;

update(r-l+1,o);

if(l<=mid)que_t(l,mid,o<<1);

if(mid=dep[top[y]])else

if(dep[x]<=dep[y])else

}void que_1(int x,int y)else

if(dep[x]<=dep[y])else

}int main();

head[u]=cnt;

e[++cnt]=(edge);

head[v]=cnt;

} dep[top[rt]=rt]=1;

fa[rt]=rt;

dfs(rt);

dfs2(rt);

for(int i=1;i<=n;++i)aa[dfn[i]]=a[i];

build(1,n,1);

while(m--)

} return 0;

}

洛谷 P3384 模板 樹鏈剖分

如題,已知一棵包含n個結點的樹 連通且無環 每個節點上包含乙個數值,需要支援以下操作 操作1 格式 1 x y z 表示將樹從x到y結點最短路徑上所有節點的值都加上z 操作2 格式 2 x y 表示求樹從x到y結點最短路徑上所有節點的值之和 操作3 格式 3 x z 表示將以x為根節點的子樹內所有節...

P3384 模板 樹鏈剖分 洛谷

題目鏈結 如題,已知一棵包含n個結點的樹 連通且無環 每個節點上包含乙個數值,需要支援以下操作 操作1 格式 1 x y z 表示將樹從x到y結點最短路徑上所有節點的值都加上z 操作2 格式 2 x y 表示求樹從x到y結點最短路徑上所有節點的值之和 操作3 格式 3 x z 表示將以x為根節點的子...

洛谷 P3384 模板 樹鏈剖分

樹鏈剖分詳情 跳轉大佬部落格 解題心得 個人看來其實樹鏈剖分就是把一棵標號沒有實際意義的樹重新標號,標號的規則按照重鏈優先,在有序之後用線段樹之類的資料結構來維護。include using namespace std const int maxn 1e5 100 struct node node ...