樹鏈剖分的基本思想是把一棵樹剖分成若干條鏈,再利用線段樹等資料結構維護相關資料,可以非常暴力優雅地解決很多問題。
樹鏈剖分中的幾個基本概念:
重兒子:對於當前節點的所有兒子中,子樹大小最大的乙個兒子就是重兒子(子樹大小相同的則隨意取乙個)
輕兒子:不是重兒子就是輕兒子
重邊:連線父節點和重兒子的邊
輕邊:連線父節點和輕兒子的邊
重鏈:相鄰重邊相連形成的鏈
值得注意的還有以下幾點:
葉子節點沒有重兒子也沒有輕兒子;
對於每一條重鏈,其起點必然是輕兒子;
單獨乙個輕葉子節點也是一條重鏈;
結合上面三條可以得出樹剖的乙個性質:重鏈必然可以囊括所有的節點。樹鏈剖分需要怎麼做呢?
1、用dfs給每乙個節點標記深度,父節點和重兒子。
2、用dfs按照dfs遍歷的順序給每乙個節點標記新的編號。關鍵點:先處理重兒子再處理輕兒子
解釋:先處理重兒子可以讓重鏈上的每乙個點的編號連續。可以觀察上圖,線上的數字就是dfs的順序。使編號連續後,我們就可以使用線段樹來維護資料了。
做完以上兩步就算是完成了樹鏈剖分了,接下來要做的就是利用其它資料結構來進行維護了。
void add(ll sta,ll to)
//鏈式前向星存樹
void dfs1(ll now,ll fa,ll deep)
}void dfs2(ll now,ll t)
}
樹上兩點的最短路徑修改操作:
void treeupd(ll x,ll y,ll num)
else
}//這乙個迴圈的目的是,只要這兩個節點不在一條重鏈上,
//就讓比較深的那乙個往上跳到另一條鏈直到兩者在同一條鏈上
//又因為節點編號是連續的,所以可以很方便地給整條鏈加上修改操作
if (id[x]<=id[y]) segupd(1,1,n,id[x],id[y],num);
else segupd(1,1,n,id[y],id[x],num);
//在最後兩者位於同一條鏈上後,仍然要對他們兩個之間的節點進行修改。
}
求和操作不再贅述,與上面的更新操作類似。
完整**
#include#include#define lson root<<1
#define rson root<<1|1
#define ll long long
#define mid ((l+r)>>1)
using namespace std;
struct data
edge[200005];
ll cnt,head[200005],f[100005],d[100005],size[100005],wson[100005],top[100005],id[100005];
ll rk[100005],tree[800005],n,m,a[100005],p,tag[800005],r,x,y,z,flag;
void add(ll sta,ll to)
void dfs1(ll now,ll fa,ll deep)
}void dfs2(ll now,ll t)
}void build(ll root,ll l,ll r)
build(lson,l,mid);
build(rson,mid+1,r);
tree[root]=(tree[lson]+tree[rson])%p;
}void push_down(ll root,ll l,ll r)
void segupd(ll root,ll l,ll r,ll al,ll ar,ll num)
else
}if (id[x]<=id[y]) sum=(sum+query(1,1,n,id[x],id[y]))%p;
else sum=(sum+query(1,1,n,id[y],id[x]))%p;
return sum;
}void treeupd(ll x,ll y,ll num)
else
}if (id[x]<=id[y]) segupd(1,1,n,id[x],id[y],num);
else segupd(1,1,n,id[y],id[x],num);
}int main()
{ scanf("%lld%lld%lld%lld",&n,&m,&r,&p);
for (ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
for (ll i=1;i練習題:
noi2015軟體包管理器
haoi2015樹上操作
zjoi2008樹的統計
luogu P3384 模板 輕重鏈剖分
有乙個樹,點有權值,要你維護一些操作。把兩點間的路徑中的點的點權都加乙個值或者詢問和。把乙個點對應的子樹中點的點權都加乙個值或者詢問和。根給出,輸出的和對乙個給出的數取模。這道題也是樹鏈剖分的模板題,之前寫過一道,樹鏈剖分怎麼做就不寫了。點這裡看 那就可以了。記得取模。include include...
樹鏈剖分 P3384 模板 樹鏈剖分
題目描述 戳這裡 題解 其實樹剖的重點就在於輕重鏈,這篇文章寫的很好 然而我線段樹寫得全是問題,改了半天2333 如下 include include include using namespace std const int maxn 100005 int n,m,root,tt,tot,lnk ...
luogu3384 樹鏈剖分
辣雞題目毀我青春!以前寫線段樹,指標從來不賦初值,偏偏這道題乖張,本來應該1a的題目我提交了29遍!我的ac率,我的時間!呃,吐槽完了。還是自己習慣不好 輕重邊剖分之後每個點的tid其實就是它的dfs序。tid陣列順著輸出其實就是樹的先序遍歷 允許我亂用概念吧 那麼以乙個節點為根節點的子樹就是剛進d...