給定一棵\(n\)個點的樹,點帶點權。
有\(m\)次操作,每次操作給定\(x,y\),表示修改點\(x\)的權值為\(y\)。
你需要在每次操作之後求出這棵樹的最大權獨立集的權值大小。
調到心態**。。。從前天晚上開始就剛這道題。。。
最大權獨立子集即「選出若干個不相鄰的點使得他們的權值最大」。——摘自akioi的神仙
若沒有修改,這道題就是樹形\(dp\)的入門題,設\(f[x][0/1]\)表示以\(x\)為根的子樹,節點\(i\)選不選的最大權獨立子集。那麼有轉移
\[f[u][0]=\sum_max(f[v][0],f[v][1])
\]\[f[u][1]=\sum_f[v][0]
\]而\(ddp\)是維護序列問題的,我們考慮將這棵樹剖一下,變成若干條重鏈計算。
那麼既然我們重鏈用矩陣乘法維護,那麼輕鏈就要先計算出來。設\(g[x][0/1]\)表示不看\(x\)的重兒子的最大權獨立子集,轉移顯然。
這樣我們就可以改寫\(f\)的轉移
\[f[u][0]=g[u][0]+max(f[v][0],f[v][1])
\]\[f[u][1]=g[u][1]+f[v][0]
\]其中\(v\)是\(u\)的重兒子。
寫成矩陣,有
\[\beginf[v][0]
\\ f[v][1]
\end
\begin
g[u][0] &g[u][0] \\
g[u][1] &-\infty
\end
=\begin
f[u][0]\\
f[u][1]
\end\]
假設我們修改點\(x\),這樣我們就可以修改\(g[top[x]]\),然後再利用\(g[top[x]]\)修改\(g[fa[top[x]]]\),再利用重鏈往上修改。
細節算是比較多吧,調的很噁心。
#include #include #include using namespace std;
const int n=100010,inf=107374323;
int head[n],a[n],f[n][2],g[n][2],son[n],rk[n],id[n],top[n],size[n],fa[n],end[n];
int n,q,tot;
struct edge
e[n*2];
void add(int from,int to)
void dfs1(int x,int father) }}
void dfs2(int x,int tp) }}
struct matrix
friend matrix operator *(matrix a,matrix b)
}m[n];
struct segtree
void build(int x,int ql,int qr)
int mid=(l[x]+r[x])>>1;
build(x*2,ql,mid); build(x*2+1,mid+1,qr);
pushup(x); }
matrix ask(int x,int ql,int qr)
void update(int x,int k)
int mid=(l[x]+r[x])>>1;
if (k<=mid) update(x*2,k);
else update(x*2+1,k);
pushup(x);
}}seg;
void update(int x,int val)
}int main()
tot=0;
dfs1(1,0); dfs2(1,1);
for (int i=1;i<=n;i++)
seg.build(1,1,n);
while (q--)
return 0;
}
洛谷P4719 模板 動態dp
大概就是一條鏈一條鏈的處理 鏈 在這裡指重鏈 對於每一條鏈,對於其上每乙個點,先算出它自身和所有輕兒子的貢獻,當做這一步中這個點的 權值 然後就變成序列上dp,直接用線段樹維護 線段樹版本o n log 2 1 include2 include3 include4 using namespace s...
洛谷P4719 動態DP模板
一棵 n 個點的樹,點帶點權。m 次操作,每次操作給定 x,y 表示修改點 x 的權值為 y 每次操作後求出這棵樹的最大權獨立集的權值大小。n,m leq 10 5 首先有乙個 o nm 的 dp 設 f u 0 1 分別表示以 u 為根的子樹中,u 不選 選 的最大獨立集權值大小。f u 0 su...
luoguP4719 模板 動態 DP
我理解的動態dp 發現dp可以寫成矩陣的形式,因此用資料結構維護矩陣乘積。對於這道題,顯然有dp f 表示 x 的子樹中,x選 不選的最大點獨立集。f sum limits max f f f sum limits f a x 既然在樹上,就用樹剖或者lct解決,本質都是將樹拆成鏈,這裡用樹剖。設 ...