(考場時只想到暴力容斥,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...