2019.01.04 洛谷P4719 【模板】動態dp(鏈分治+ddp)
阿新 • • 發佈:2019-01-08
傳送門
模板題。
題意簡述:給你一棵樹,支援修改一個點,維護整棵樹的最大帶權獨立集。
思路:
我們考慮如果沒有修改怎麼做。
貌似就是一個
樹形
,
表示不選
的最大值,
表示選
的最大值。
那麼可以這樣從
的兒子
轉移過來:
然後就能夠解決靜態時候的問題。
然而現在是動態的怎麼搞呢?
有句話說得好:智商不夠資料結構來湊,這個時候就可以上樹鏈剖分辣。
我們知道有點分治這種東西,同樣也有鏈分治這種東西,也就是對於一條鏈維護其有關資訊。
對於這道題,我們考慮維護重鏈的資訊,考慮記一個
表示選/不選
點時所有輕兒子的子孫的最大獨立集。
於是
然後這個東西是可以用類似於矩陣乘法的東西轉移的,我們將矩陣的加法重定義成取 ,把矩陣的乘法重定義成加法。
然後就能夠變成下面的矩陣轉移:
由於矩陣滿足結合律。
所以可以上樹剖,維護區間矩陣乘積。
修改的時候,從被修改節點開始沿著重鏈向根節點跳。對於每條重鏈,先對鏈底修改,然後更新鏈頂,然後跳到下一條重鏈繼續修改即可。
每次答案就是根節點的
值。
程式碼:
#include<bits/stdc++.h>
#define lc (p<<1)
#define rc (p<<1|1)
#define ri register int
#define mid (l+r>>1)
using namespace std;
const int N=1e5+5;
struct Mat{
int g[2][2];
Mat(){g[0][0]=g[0][1]=g[1][0]=g[1][1]=0;}
friend inline Mat operator*(const Mat&a,const Mat&b){
Mat ret;
for(ri i=0;i<2;++i)for(ri k=0;k<2;++k)for(ri j=0;j<2;++j)ret.g[i][j]=max(ret.g[i][j],a.g[i][k]+b.g[k][j]);
return ret;
}
}T[N<<2],val[N];
int n,m,tot=0,siz[N],a[N],hson[N],top[N],bot[N],pred[N],num[N],f[N][2],fa[N];
vector<int>e[N];
void dfs1(int p){
siz[p]=1,f[p][0]=0,f[p][1]=a[p];
for(ri v,i=0;i<e[p].size();++i){
v=e[p][i];
if(v==fa[p])continue;
fa[v]=p,dfs1(v),siz[p]+=siz[v];
if(siz[v]>siz[hson[p]])hson[p]=v;
f[p][0]+=max(f[v][0],f[v][1]),f[p][1]+=f[v][0];
}
}
void dfs2(int p,int tp){
top[p]=tp,pred[num[p]=++tot]=p;
if(!hson[p]){bot[tp]=tot;return;}
dfs2(hson[p],tp);
for(ri v,i=0;i<e[p].size();++i){
v=e[p][i];
if(v!=fa[p]&&v!=hson[p])dfs2(v,v);
}
}
inline void build(int p,int l,int r){
if(l==r){
int u=pred[l],g0=0,g1=a[u];
for(ri v,i=0;i<e[u].size();++i){
v=e[u][i];
if(v!=hson[u]&&v!=fa[u])g0+=max(f[v][0],f[v][1]),g1+=f[v][0];
}
val[l].g[0][0]=val[l].g[0][1]=g0,val[l].g[1][0]=g1,val[l].g[1][1]=-0x3f3f3f3f,T