題目大意:已知一棵包含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 ...