今天早上换寝室,耽误了一些时间。还是继续复习动态规划。
单调队列优化DP
第一道,宝物筛选。一道多重背包优化题。如果用二进制优化很好做,但时间复杂度是O(nW*logm)。单调队列优化做法如下:
首先做出普通的多重背包的转移方程:f[j]=max{f[j-w*k]+v*k},w为重量,v为价值。
使得j=aw+b,即a=j/w,b=j%w。原方程可转化为f[j]=max{f[(a-k)*w+b]-(a-k)*v}+a*v。
这个时候我们就可以枚举b,将a-k看做整体,对于每个b都对f[(a-k)*w+b]-(a-k)*v做单调队列,再将a从大到小循环就可以了。
方程的转化比较难想到,代码的具体实现如下:

#include<bits/stdc++.h> using namespace std; const int N=4e4+1e3; int n,W,p[N]; long long f[N],q[N]; long long Max(long long x,long long y){ return x>y?x:y; } int Min(int x,int y){ return x<y?x:y; } int rd(){ int s=0,ff=1; char w=getchar(); while(!isdigit(w)) ff^=w=='-',w=getchar(); while(isdigit(w)) s=s*10+w-'0',w=getchar(); return ff?s:-s; } int main(){ int v,w,m,l,r,a; long long x; n=rd(); W=rd(); for(int i=1;i<=n;i++){ v=rd(),w=rd(),m=rd(); for(int b=0;b<w;b++){ l=1,r=0,a=(W-(W%w-b+w)%w)/w; for(int k=1;k<=Min(a,m);k++){ x=f[(a-k)*w+b]-(a-k)*v; while(l<=r&&q[r]<=x) r--; q[++r]=x,p[r]=a-k; } for(int j=W-(W%w-b+w)%w;j!=b;j-=w){ a=j/w; while(p[l]>=a) l++; f[j]=Max(f[j],q[l]+a*v); if(a>=m+1) x=f[(a-m-1)*w+b]-(a-m-1)*v; while(l<=r&&q[r]<=x) r--; q[++r]=x,p[r]=a-m-1; } } } printf("%lld ",f[W]); return 0; }
数据结构优化DP
第一道,"动态 DP"&动态树分治。剩下的大多数时间都花在上面了,头疼。
动态DP具体的思想和做法看这篇博客吧,个人觉得写的非常好。
我在这里讲下操作的具体实现(为了方便表达,把新定义的符号叫做乘号):
对于点x,它的权值要改为y,首先它这个点的矩阵要先修改(别忘了权值要改)。(注意这个点的矩阵和线段树上的矩阵是两个矩阵)
然后进入while循环,先求出Top[x](Top[x]为x所在重链的顶点)的f值,求法是对x这一整条重链即线段树上的连续一段求矩阵的连乘,因为叶子节点的f值等于g值,所以乘出来后矩阵第1行第1列就是f[Top[x]][0],第2行第1列就是f[Top[x]][1]。
再用x这个点的矩阵更新线段树上x的矩阵,之后再求出Top[x]的f值。
再就可以用Top[x]的新旧f值修改树上“Top[x]的父亲”这个点的矩阵(具体实现看代码,好理解的)。然后x=“Top[x]的父亲”。
然后while循环结束了,不用担心Top[x]的父亲也就是现在的x没有在线段树上更新,因为在下个循环会更新到的。
答案就是求出1的f值,取最大值输出即可。代码具体实现如下:

#include<bits/stdc++.h> using namespace std; const int N=1e5+1e4; int n,m,cnt,tot,h[N],d[N],f[N][2],g[N][2],fa[N],dep[N],siz[N],Son[N],Top[N],dfn[N],sfn[N],End[N]; int Max(int x,int y){ return x>y?x:y; } struct nod{ int nxt,to; }a[N*2]; struct Num{ int p[3][3]; Num(){ memset(p,0,sizeof(p)); p[2][2]=-1e8; } inline Num operator*(Num b){ Num c; for(int i=1;i<=2;i++) for(int j=1;j<=2;j++) for(int k=1;k<=2;k++) c.p[i][j]=Max(c.p[i][j],p[i][k]+b.p[k][j]); return c; } }b[N*4],c[N],ans; int rd(){ int s=0,ff=1; char w=getchar(); while(!isdigit(w)) ff^=w=='-',w=getchar(); while(isdigit(w)) s=s*10+w-'0',w=getchar(); return ff?s:-s; } void Add(int x,int y){ a[++cnt].nxt=h[x]; a[cnt].to=y; h[x]=cnt; } void Dfs(int x,int w){ int y; siz[x]=1; for(int i=h[x];i;i=a[i].nxt){ y=a[i].to; if(y==w) continue ; fa[y]=x; dep[y]=dep[x]+1; Dfs(y,x); siz[x]+=siz[y]; if(siz[y]>siz[Son[x]]) Son[x]=y; } } void Dfss(int x,int w){ int y; dfn[++tot]=x; sfn[x]=tot; if(Son[x]) Top[Son[x]]=Top[x],Dfss(Son[x],x),End[x]=End[Son[x]]; else End[x]=x; for(int i=h[x];i;i=a[i].nxt){ y=a[i].to; if(y==w||y==Son[x]) continue ; Top[y]=y; Dfss(y,x); } } void Dfs_dp(int x,int w){ int y; f[x][0]=g[x][0]=0; f[x][1]=g[x][1]=d[x]; for(int i=h[x];i;i=a[i].nxt){ y=a[i].to; if(y==w) continue ; Dfs_dp(y,x); f[x][0]+=Max(f[y][0],f[y][1]),f[x][1]+=f[y][0]; if(y!=Son[x]) g[x][0]+=Max(f[y][0],f[y][1]),g[x][1]+=f[y][0]; } } void Build(int x,int l,int r){ if(l==r){ b[x].p[1][1]=g[dfn[l]][0]; b[x].p[1][2]=g[dfn[l]][0]; b[x].p[2][1]=g[dfn[l]][1]; b[x].p[2][2]=-1e8; c[dfn[l]]=b[x]; return ; } int mid=(l+r)>>1; Build(x<<1,l,mid); Build(x<<1|1,mid+1,r); b[x]=b[x<<1]*b[x<<1|1]; } void Update(int x,int l,int r,int s){ if(l==r){ b[x]=c[dfn[l]]; return ; } int mid=(l+r)>>1; if(s<=mid) Update(x<<1,l,mid,s); else Update(x<<1|1,mid+1,r,s); b[x]=b[x<<1]*b[x<<1|1]; } Num Query(int x,int l,int r,int L,int R){ if(L<=l&&r<=R) return b[x]; int mid=(l+r)>>1; if(mid>=R) return Query(x<<1,l,mid,L,R); else if(mid<L) return Query(x<<1|1,mid+1,r,L,R); else return Query(x<<1,l,mid,L,R)*Query(x<<1|1,mid+1,r,L,R); } int main(){ int x,y,z; Num A,B; n=rd(); m=rd(); for(int i=1;i<=n;i++) d[i]=rd(); for(int i=1;i<n;i++) x=rd(),y=rd(),Add(x,y),Add(y,x); dep[1]=1; Dfs(1,0); Top[1]=1; Dfss(1,0); Dfs_dp(1,0); Build(1,1,n); while(m--){ x=rd(),y=rd(); c[x].p[2][1]+=y-d[x]; d[x]=y; while(x){ A=Query(1,1,n,sfn[Top[x]],sfn[End[x]]); Update(1,1,n,sfn[x]); B=Query(1,1,n,sfn[Top[x]],sfn[End[x]]); x=fa[Top[x]]; c[x].p[1][1]+=Max(B.p[1][1],B.p[2][1])-Max(A.p[1][1],A.p[2][1]); c[x].p[1][2]+=Max(B.p[1][1],B.p[2][1])-Max(A.p[1][1],A.p[2][1]); c[x].p[2][1]+=B.p[1][1]-A.p[1][1]; } A=Query(1,1,n,sfn[1],sfn[End[1]]); printf("%d ",Max(A.p[1][1],A.p[2][1])); } return 0; }
今天就这样吧,看了几题比较简单就没写了,明天再说。