點此看題
動態 \(dp\) 的思路主要是用矩陣乘法加速 \(dp\),所以首先要知道矩陣乘法的擴充套件版:
\[c(i,k)=\max\
\]令人震驚的是上面這東西也滿足結合律,現在我們來證明一下,假設有三個矩陣 \(a,b,c\) 相乘,大小分別是 \(n\times m,m\times p,p\times q\),我們把最終某乙個位置上的值暴力展開:
\[(i,j)=\max_^ma(i,k)+\big(\max_^p b(k,l)+c(l,j)\big)
\]\[=\max_^m\max_^p a(i,k)+b(k,l)+c(l,j)
\]\[=\max_^p\big(\max_^m a(i,k)+b(k,l)\big)+c(l,j)
\]所以先乘 \(a,b\) 還是先乘 \(b,c\) 對答案沒有影響,結合律得證。
首先寫出暴力的 \(dp\) 柿子,設 \(f(u,0/1)\) 表示 \(u\) 這個點不選\(/\)選的最大權值,轉移:
\[f(u,0)=\sum\max(f(v,0),f(v,1))
\]\[f(u,1)=a(u)+\sum f(v,0)
\]先來考慮一下鏈怎麼做,我們構造乙個像這樣的轉移矩陣:
\[\left(\begin0&0\\a(u)&-\infty\end\right)\times\left(\beginf(v,0)\\f(v,1)\end\right)=\left(\beginf(u,0)\\f(u,1)\end\right)
\]然後要求根的 \(dp\) 值就直接把所有矩陣乘起來就行了,時間複雜度 \(o(n\log n)\)
那麼我們能不能把上面的做法搬到樹上呢?考慮把樹剖分成鏈然後套上面的做法,也就是用樹鏈剖分。每個點的轉移矩陣就針對他的重兒子來定義,但同時我們要考慮輕兒子對他 \(dp\) 值的貢獻,所以再定義 \(f'(u,0/1)\) 表示 \(u\) 不選\(/\)選,考慮 \(u\) 和 \(u\) 的所有輕兒子的最大值,那麼有如下轉移:
\[f(u,0)=f'(u,0)+\max\
\]\[f(u,1)=f'(u,1)+f(son,0)
\]寫成矩陣就是這個樣子的:
\[\left(\beginf'(u,0)&f』(u,0)\\f'(u,1)&-\infty\end\right)\times\left(\beginf(son,0)\\f(son,1)\end\right)=\left(\beginf(u,0)\\f(u,1)\end\right)
\]先考慮怎麼統計答案,我們找到根所在的那條重鏈,把所有轉移矩陣乘起來就行了。
再考慮如何修改,修改乙個點的點權只會對它的祖先產生影響。而且由於路徑上只有 \(o(\log n)\) 條輕邊,所以一共只需要改 \(o(\log n)\) 個矩陣,這部分可以看看**:
void modify(int u,int w)//把u點權改成w
}
用乙個線段樹維護矩陣套上樹鏈剖分:\(o(2^3\cdot n\log^2 n)\)
#include #include using namespace std;
const int m = 100005;
const int inf = 1e9;
int read()
while(c>='0' && c<='9')
return x*f;
}int n,m,tot,cnt,f[m],a[m],id[m],fa[m];
int siz[m],son[m],num[m],top[m],bot[m],dp[m][2];
//top表示重鏈頭
//bot表示重鏈尾
//id表示線段樹上位置所對應的點
//dp表示初始的dp陣列
struct edge
}e[2*m];
struct matrix
matrix operator * (const matrix &b) const
void print()
}val[m],tr[4*m];
//線段樹部分
void up(int i)
void build(int i,int l,int r)
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
up(i);
}void upd(int i,int l,int r,int x)//修改x這個位置的矩陣
int mid=(l+r)>>1;
if(mid>=x) upd(i<<1,l,mid,x);
else upd(i<<1|1,mid+1,r,x);
up(i);
}matrix ask(int i,int l,int r,int l,int r)
//樹鏈剖分部分
void dfs1(int u,int p)
}void dfs2(int u,int tp)
else bot[u]=u;//如果沒有重兒子底部就是自己
for(int i=f[u];i;i=e[i].next) }
void modify(int u,int w)//把u點權改成w
}signed main()
dfs1(1,0);
dfs2(1,1);
build(1,1,n);
while(m--)
}
模板 動態 DP 動態樹分治
題面鏈結 include define ll long long define rg register using namespace std templateinline void read t x templateinline void write t x if x 0 x x,putchar ...
模板 動態規劃 數字dp
includeusing namespace std define ll long long int a 20 ll dp 20 20 可能需要的狀態1 20 可能需要的狀態2 不同題目狀態不同 ll dfs int pos,int state1 可能需要的狀態1 int state2 可能需要的狀...
luoguP4719 模板 動態 DP
我理解的動態dp 發現dp可以寫成矩陣的形式,因此用資料結構維護矩陣乘積。對於這道題,顯然有dp f 表示 x 的子樹中,x選 不選的最大點獨立集。f sum limits max f f f sum limits f a x 既然在樹上,就用樹剖或者lct解決,本質都是將樹拆成鏈,這裡用樹剖。設 ...