【洛谷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 }