NOI2020命運 線段樹合併優化樹形DP

2022-06-03 18:48:10 字數 2691 閱讀 4447

(考場時只想到暴力容斥,24分滾了)

題目:dp方程怎麼來就不寫了,本文重點分析如何用線段樹合併及正確性

dp方程為:令dp[u][h]表示u的子樹中上端點已經處理好了,下段點在子樹中的,上段點最深為h的方案數,特別的,如果h==0,則代表已經處理好了

每當遇到乙個兒子v時都更新一遍dp陣列

dp』[u][h] = dp[v][h] * sum[u][h-1] + dp[u][h] * (sum[v][h] + sum[v][dep[u]); 

考慮線段樹合併(以下子樹)

我們可以便遍歷邊更新各式的值

我們開n顆線段樹,下標 i 維護dp[u][i],維護區間和,維護區間乘積

不妨令s1 = sum[u][h-1],s2 = (sum[v][h] + sum[v][dep[u][)

我們只要討論4種情況

(i) (!u && !v)很顯然這種情況可以直接返回

(ii)  (!u || !v)

即合併中有其中一顆樹沒有子樹了,反映在dp方程其實就是這一整塊都沒值了,都不合法

這就意味著u或者v(沒有值的那個)的sum在這段區間不會再變了,變成常量,於是就可以愉快地區間乘法了

(iii)(l == r)

這就意味遍歷到葉子節點,這是最簡單的,直接套公式就可以了

(iv)

otherwise

即既有左子樹又有右子樹

那我們只需要遍歷左子樹完遍歷右子樹即可

為什麼是對的呢

因為我們處理左子樹的答案,s1,s2已經被左子樹的字首和更新了,在處理右子樹的答案時,同時考慮了左邊的貢獻

類似於cdq分治的思想

然後就做完了(本蒟覺得還是一道很妙的題)

**如下

/*

命運*/

#include

#include

#include

#include

using

namespace

std;

#define ll long long

#define mod 998244353

const

int maxn = 5e5 + 10

;int add(int x,int

y)int

rt[maxn],h[maxn];

struct

segmenttreet[maxn

<<5

];int cnt = 0

;void pushup(int

p)void pushdown(int

p)void ins(int &p,int l,int r,int pos,int

v)

int mid = (l + r) >> 1

;

if(pos <=mid) ins(lc(p),l,mid,pos,v);

else ins(rc(p),mid+1

,r,pos,v);

//pushup(p);

}ll query(

int p,int l,int r,int a,int

b) pushdown(p);

ll ans = 0

;

int mid = (l + r) >> 1

;

if(a <= mid) ans =add(ans,query(lc(p),l,mid,a,b));

if(b > mid) ans = add(ans,query(rc(p),mid+1

,r,a,b));

return

ans;

}int merge(int u,int v,int l,int r,ll &s1,ll &s2)

s1 =add(s1,sum(u));

sum(u) = sum(u) * s2 % mod,lzy(u) = lzy(u) * s2 %mod;

return

u; }

if(l ==r)

pushdown(u),pushdown(v);

int mid = (l + r) >> 1

; lc(u) =merge(lc(u),lc(v),l,mid,s1,s2);

rc(u) = merge(rc(u),rc(v),mid+1

,r,s1,s2);

pushup(u);

returnu;}

intread()

struct

edgeedge[maxn*2

];int tot = 0

;int

dep[maxn];

intn;

inthead[maxn];

void add(int x,int

y)void dfs(int x,int

fa)}

void treedp(int u,int

fa)}

intmain()

dfs(

1,0);

int m =read();

for(int i = 1; i <= m; ++i)

treedp(

1,0);

printf(

"%lld\n

",query(rt[1],0,n,0,0

));

return0;

}

NOI2020 命運(線段樹合併優化dp)

給定一棵 n 個點 n 1 條邊的樹,1 號節點為根節點,樹上每條邊都可以取 0 或者 1 有 m 對限制,每對限制 u,v 表示 u 到 v 的路徑上至少有一條邊的值為 1 保證 u 為 v 的祖先,問有多少中合法的方案對於所有 m 個限制都滿足 n,m leq 5 cdot 10 容易想到容斥,...

線段樹合併

做永無鄉的時候,以為是主席樹合併,後來感覺不對勁,唔。x和y是兩顆樹的根。這個演算法是從歸併演算法那引申的。實際運作的時候,考慮到了線段樹的本質 線段樹有效節點就是葉子節點。好像是句廢話。其實不是,這句話啟發我們並不需要合併一整棵樹,我們只需要處理好葉子節點,考慮把y樹合併到x上,那麼把y樹的葉子節...

線段樹合併

今天寫dsu on tree 的時候發現不會寫線段樹合併,於是滾來寫線段樹合併部落格 對於值域相同的兩個權值線段樹x xx和y yy 假設把y yy合併到x xx上 每個節點有兩種情況 其中至少有乙個節點沒有權值 x y x y x y 直接x x y x x y x x y x 0?y x x 0...