【洛谷P4719】動態dp 動態dp模板
題目大意:給你一顆$n$個點的樹,點有點權,有$m$次操作,每次操作給定$x$,$y$,表示修改點$x$的權值為$y$。
你需要在每次操作之後求出這棵樹的最大權獨立集的權值大小。
數據範圍:$n,m≤1e5$
我們顯然可以得出一個$O(nm)$的暴力做法,每次修改完後$dp$一次,然而這個顯然會超時。
考慮當樹退化成鏈時的簡單做法。
我們用線段樹維護每個區間的答案。對於區間$[l,r]$,我們維護一個$2×2$的答案矩陣$ans$。
設$ans[0][0]$表示區間左端點可能被選擇,右端點一定不被選擇時的最大值。
設$ans[0][1]$表示區間左端點可能被選擇,右端點可能被選擇時的最大值。
設$ans[1][0]$表示區間左端點一定不被選擇,右端點一定不被選擇時的最大值。
設$ans[1][1]$表示區間左端點一定不被選擇,右端點可能被選擇時的最大值。
對於葉節點,顯然$ans[0][0]=ans[0][1]=0$,$ans[1][0]=val[x]$,$ans[1][1]=-INF$。
考慮已經求出$[l,mid]$,$[mid+1,r]$兩個區間的答案,如何合並出$[l,r]$的答案。
不難發現$ans[0][0]=max(ansl[0][0]+ansr[0][0],ansl[0][1]+ansr[1][0])$;
$ans[0][1],ans[1][0],ans[1][1]$的轉移都長得差不多。
每次修改一個權值,我們可以通過$pushup$更新區間的答案矩陣。
考慮把這個做法擴展到樹上,我們通過樹鏈剖分將整棵樹剖成若幹條鏈,對於每一條鏈我們就用如上所述的方式進行維護。對於接有輕兒子的鏈上節點,我們將輕兒子產生的共吸納累計,將其加到鏈上節點上即可。
(細節可以看代碼)
時間復雜度:$O(m log^2n)$。
1 #include<bits/stdc++.h>
2 #define ls (x<<1)
3 #define rs (x<<1|1)
4 #define mid ((a[x].l+a[x].r)>>1)
5 #define L long long
6 #define INF 1000000007
7 #define M 100005
8 using namespace std;
9
10 struct edge{int u,next;}e[M*2]={0}; int head[M]={0},use=0;
11 void add(int x,int y){use++;e[use].u=y;e[use].next=head[x];head[x]=use;}
12 int val[M]={0},n,m,f[M][2]={0};
13
14 int siz[M]={0},son[M]={0},dn[M]={0},top[M]={0},dfn[M]={0},rec[M]={0},fa[M]={0},t=0;
15
16 void dfs1(int x){
17 siz[x]=1;
18 for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa[x]){
19 fa[e[i].u]=x;
20 dfs1(e[i].u);
21 siz[x]+=siz[e[i].u];
22 if(siz[son[x]]<siz[e[i].u]) son[x]=e[i].u;
23 }
24 }
25 void dfs2(int x,int Top){
26 top[x]=Top; dfn[x]=++t; rec[t]=x;
27 if(son[x]) dfs2(son[x],Top),dn[x]=dn[son[x]];
28 else dn[x]=x;
29 for(int i=head[x];i;i=e[i].next)
30 if(e[i].u!=fa[x]&&e[i].u!=son[x])
31 dfs2(e[i].u,e[i].u);
32 }
33 void dp(int x,int fa){
34 f[x][1]=val[x];
35 for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa){
36 dp(e[i].u,x);
37 f[x][0]+=max(f[e[i].u][0],f[e[i].u][1]);
38 f[x][1]+=f[e[i].u][0];
39 }
40 }
41
42 struct mat{
43 int a[2][2]; mat(){memset(a,0,sizeof(a));}
44 mat(int x){a[0][0]=a[0][1]=a[1][0]=a[1][1]=x;}
45 mat(int a1,int a2,int a3,int a4){a[0][0]=a1; a[0][1]=a2; a[1][0]=a3; a[1][1]=a4;}
46 friend mat operator *(mat a,mat b){
47 mat c=-INF;
48 for(int i=0;i<2;i++)
49 for(int j=0;j<2;j++)
50 for(int k=0;k<2;k++)
51 c.a[i][j]=max(c.a[i][j],a.a[i][k]+b.a[k][j]);
52 return c;
53 }
54 }wei[M];
55 struct seg{int l,r;mat a;}a[M<<2];
56 void pushup(int x){a[x].a=a[ls].a*a[rs].a;}
57
58 void build(int x,int l,int r){
59 a[x].l=l; a[x].r=r;
60 if(l==r){
61 int u=rec[l],g0=0,g1=val[u];
62 for(int i=head[u];i;i=e[i].next)
63 if(e[i].u!=son[u]&&e[i].u!=fa[u]){
64 g0+=max(f[e[i].u][0],f[e[i].u][1]);
65 g1+=f[e[i].u][0];
66 }
67 a[x].a=wei[l]=mat(g0,g0,g1,-INF);
68 return;
69 }
70 build(ls,l,mid);
71 build(rs,mid+1,r);
72 pushup(x);
73 }
74 void updata(int x,int k){
75 if(a[x].l==a[x].r) return void(a[x].a=wei[k]);
76 if(k<=mid) updata(ls,k);
77 else updata(rs,k);
78 pushup(x);
79 }
80
81 mat query(int x,int l,int r){
82 if(l<=a[x].l&&a[x].r<=r) return a[x].a;
83 mat res;
84 if(l<=mid) res=query(ls,l,r);
85 if(mid<r){
86 if(l<=mid) return res*query(rs,l,r);
87 return query(rs,l,r);
88 }else return res;
89 }
90 mat query(int x){return query(1,dfn[top[x]],dfn[dn[x]]);}
91 void solve(){mat hh=query(1); printf("%d\n",max(hh.a[0][0],hh.a[1][0]));}
92
93 void Updata(int x,int Val){
94 wei[dfn[x]].a[1][0]+=Val-val[x]; val[x]=Val;
95 while(x){
96
97 mat last=query(x);
98 int lg0=max(last.a[0][0],last.a[1][0]),lg1=last.a[0][0];
99
100 updata(1,dfn[x]);
101
102 mat now=query(x);
103 int ng0=max(now.a[0][0],now.a[1][0]),ng1=now.a[0][0];
104
105 x=fa[top[x]]; if(!x) return;
106
107 int g0=ng0-lg0,g1=ng1-lg1;
108 wei[dfn[x]].a[0][0]+=g0;
109 wei[dfn[x]].a[0][1]+=g0;
110 wei[dfn[x]].a[1][0]+=g1;
111 }
112 }
113
114 int main(){
115 //freopen("in.txt","r",stdin);
116 scanf("%d%d",&n,&m);
117 for(int i=1;i<=n;i++) scanf("%d",val+i);
118 for(int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
119 dfs1(1);
120 dfs2(1,1);
121 dp(1,0);
122 build(1,1,n);
123 //solve();
124 while(m--){
125 int x,y; scanf("%d%d",&x,&y);
126 Updata(x,y);
127 solve();
128 }
129 }
【洛谷P4719】動態dp 動態dp模板