P4719 【模板】动态dp
生活随笔
收集整理的這篇文章主要介紹了
P4719 【模板】动态dp
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
[題目鏈接] https://www.luogu.org/problemnew/show/P4719
題解(shadowice)
代碼借鑒
求最大獨立集是一個經典的問題,這個轉移如果套上樹剖就可以轉化為序列上的問題,就可以用所謂的矩陣乘法維護了.
設\(g[x][0/1]\)代表重兒子以外的答案,\(f[x][0/1]\)代表最終的答案,假如維護好了這兩個信息,那么重鏈上的轉移就是正確的了.
注意矩陣乘法的順序,手模可以發現答案是重鏈上從上到下的g[]相乘再乘上最下面的點的\(f[]\).而最底下的點的\(f[]\)恰好就是\(g[]\),而乘的過程中只要滿足\(g[]\)的\(a[0][0]\)和\(a[1][0]\)正確就行了,所以最終答案就可以直接是\(rt\)所在的重鏈的矩陣乘起來.
dp用矩陣乘法維護可以參考這道題 .取max操作也可以用矩陣維護.
具體實現:修改某一個點權值就把它的信息更改,直接更新它所在的重鏈的答案,再一路更新上面所有的重鏈.
注意幾點:
\(1.\)證明正確性的時候,一定要把矩陣乘法的順序手模出來,一般要轉化為與樹剖的dfn序相符.
\(2.\)初始的答案在建樹時存進去,所以還要先一遍dfs算出最初的答案.
細節詳見代碼
#include<bits/stdc++.h> using namespace std; typedef long long LL; const LL INF=1e18+7; inline LL read(){register LL x=0,f=1;register char c=getchar();while(c<48||c>57){if(c=='-')f=-1;c=getchar();}while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar();return f*x; }const int MAXN=100005; const int MAXM=200005;struct Edge{int v,next; }e[MAXM]; int first[MAXN],Ecnt=0; inline void Add_edge(int u,int v){e[++Ecnt]=(Edge){v,first[u]};first[u]=Ecnt; }int size[MAXN],top[MAXN],seg[MAXN],rev[MAXN],son[MAXN],fa[MAXN],bot[MAXN],val[MAXN]; LL f[MAXN][2]; int n,m;inline void dfs1(int u,int pre){size[u]=1;for(int i=first[u];i;i=e[i].next){int v=e[i].v;if(v==pre) continue;fa[v]=u;dfs1(v,u);size[u]+=size[v];if(size[v]>size[son[u]]) son[u]=v;} }inline void dfs2(int u,int pre){seg[u]=++seg[0];rev[seg[0]]=u;if(son[u]){top[son[u]]=top[u];dfs2(son[u],u);bot[u]=bot[son[u]];//bot[]的更新}else bot[u]=u;for(int i=first[u];i;i=e[i].next){int v=e[i].v;if(v==pre||v==son[u]) continue; top[v]=v;dfs2(v,u); } }inline void dfs3(int u,int pre){f[u][1]=val[u];for(int i=first[u];i;i=e[i].next){int v=e[i].v;if(v==pre) continue;dfs3(v,u);f[u][1]+=f[v][0];f[u][0]+=max(f[v][0],f[v][1]);//算出初始答案} }struct Matrix{LL a[2][2];//這題矩陣乘法可以直接這么寫inline friend Matrix operator * (Matrix a,Matrix b){Matrix res;res.a[0][0]=max(a.a[0][0]+b.a[0][0],a.a[0][1]+b.a[1][0]);res.a[0][1]=max(a.a[0][0]+b.a[0][1],a.a[0][1]+b.a[1][1]);res.a[1][0]=max(a.a[1][0]+b.a[0][0],a.a[1][1]+b.a[1][0]);res.a[1][1]=max(a.a[1][0]+b.a[0][1],a.a[1][1]+b.a[1][1]);return res;} }g[MAXN<<2],tmp[MAXN];struct SGT{ #define ls (rt<<1) #define rs (rt<<1|1)inline void pushup(int rt){g[rt]=g[ls]*g[rs];}inline void build(int rt,int l,int r){if(l==r){int u=rev[l],g0=0,g1=val[u];for(int i=first[u];i;i=e[i].next){int v=e[i].v;if(v==son[u]||v==fa[u]) continue;g0+=max(f[v][0],f[v][1]);g1+=f[v][0];}tmp[l]=g[rt]=(Matrix){g0,g0,g1,-INF};//建樹時就要算出初始的答案!!!return;}int mid=(l+r)>>1;build(ls,l,mid);build(rs,mid+1,r);pushup(rt);}inline Matrix query(int rt,int l,int r,int x,int y){if(l>=x&&r<=y) return g[rt];int mid=(l+r)>>1;if(y<=mid) return query(ls,l,mid,x,y);if(mid<x) return query(rs,mid+1,r,x,y);return query(ls,l,mid,x,y)*query(rs,mid+1,r,x,y);}inline Matrix GetMat(int u){return query(1,1,n,seg[top[u]],seg[bot[u]]);}inline void modify(int rt,int l,int r,int p){if(l==r&&l==p){g[rt]=tmp[l];return;}int mid=(l+r)>>1;if(p<=mid) modify(ls,l,mid,p);else modify(rs,mid+1,r,p);pushup(rt);}inline void modify(int u,int w){tmp[seg[u]].a[1][0]+=w-val[u],val[u]=w;//細節處理,先更新再說while(u){Matrix a=GetMat(u);modify(1,1,n,seg[u]);//算出u所在的重鏈的答案Matrix b=GetMat(u);u=fa[top[u]];//一路更新上去.if(!u) break;//利用更新前后的差值去更新fa//tmp是一個中間變量,方便更新g[]tmp[seg[u]].a[0][1]=(tmp[seg[u]].a[0][0]+=max(b.a[0][0],b.a[1][0])-max(a.a[0][0],a.a[1][0]));tmp[seg[u]].a[1][0]+=b.a[0][0]-a.a[0][0];}} #undef ls #undef rs }T;int main(){ // freopen("asd.in","r",stdin);n=read(),m=read();for(int i=1;i<=n;i++)val[i]=read();for(int i=1;i<=n-1;i++){int x=read(),y=read();Add_edge(x,y);Add_edge(y,x);//記得建雙向邊}dfs1(1,0);top[1]=1;dfs2(1,0);dfs3(1,0);T.build(1,1,n);for(int i=1;i<=m;i++){int x=read(),y=read();T.modify(x,y);Matrix ans=T.GetMat(1);//答案就轉化為所有的g[]乘起來printf("%lld\n",max(ans.a[0][0],ans.a[1][0]));} }轉載于:https://www.cnblogs.com/lizehon/p/10428349.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的P4719 【模板】动态dp的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 遍历xml所有节点
- 下一篇: 【idea】idea如何配置JDK(转)