Luogu P3384 樹鏈剖分模板

2022-06-06 07:51:05 字數 2423 閱讀 1022

樹鏈剖分的基本思想是把一棵樹剖分成若干條鏈,再利用線段樹等資料結構維護相關資料,可以非常暴力優雅地解決很多問題。

樹鏈剖分中的幾個基本概念:

重兒子:對於當前節點的所有兒子中,子樹大小最大的乙個兒子就是重兒子(子樹大小相同的則隨意取乙個)

輕兒子:不是重兒子就是輕兒子

重邊:連線父節點和重兒子的邊

輕邊:連線父節點和輕兒子的邊

重鏈:相鄰重邊相連形成的鏈

值得注意的還有以下幾點:

葉子節點沒有重兒子也沒有輕兒子;

對於每一條重鏈,其起點必然是輕兒子;

單獨乙個輕葉子節點也是一條重鏈;

結合上面三條可以得出樹剖的乙個性質:重鏈必然可以囊括所有的節點。樹鏈剖分需要怎麼做呢?

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...