bzoj4182 Shopping
点分治+单调队列优化多重背包+dfs序优化背包合并
连通块算是一种依赖背包
但是背包合并是O(m^2)的。因为x必须考虑之前儿子的选法
trick:dfs序优化
点分治统计过G的连通块,以G为根找到dfs序
子树连续一段,不选择x,x的子树都不能选,选择x才考虑子树怎么选
倒序dfs序处理,每次加入一个x物品,而不是背包合并。因为我们点分治考虑的是G为根,所以x不会为根,只用考虑x对全局的贡献
f[x][*]<-f[fdfn[dfn[x]+1]][*] || f[fdfn[dfn2[x]+1]][*]
意义是考虑到x,dfs序中前dfn[x]个做背包的结果,x不选择保证不从子树位置转移,所以选择的情况一定是一个连通块样子
(点分治换成dot可以牺牲一个log搞出以x为根dfs序,算出x为根的连通块答案)
代码:
注意,多组数据vis清空,sz重新找,单调队列大小是m
#include<bits/stdc++.h> #define reg register int #define il inline #define fi first #define se second #define mk(a,b) make_pair(a,b) #define numb (ch^'0') using namespace std; typedef long long ll; template<class T>il void rd(T &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');} template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');} template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');} namespace Miracle{ const int N=505; const int M=4004; const int inf=0x3f3f3f3f; int n,m; int w[N],c[N],d[N]; int ans; struct node{ int nxt,to; }e[2*N]; int hd[N],cnt; void add(int x,int y){ e[++cnt].nxt=hd[x]; e[cnt].to=y; hd[x]=cnt; } int sz[N]; int nowsz,rt; bool vis[N]; void fin(int x,int fa){ sz[x]=1; int mxsz=0; for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(y==fa||vis[y]) continue; fin(y,x); sz[x]+=sz[y]; mxsz=max(mxsz,sz[y]); } mxsz=max(mxsz,nowsz-sz[x]); if(mxsz<=nowsz/2) rt=x; } int dfn[N],dfn2[N],df; int fdfn[N]; int f[N][M]; int q[M],l,r; void wrk(int x){ // memset(f[x],0,sizeof f[x]); // cout<<" wrk x "<<x<<" c "<<c[x]<<" d "<<d[x]<<" w "<<w[x]<<" d[133] "<<d[133]<<endl; for(reg i=0;i<=m;++i) f[x][i]=f[fdfn[dfn2[x]+1]][i]; int y=fdfn[dfn[x]+1]; /// cout<<"with "<<y<<endl; int lp=0; for(reg yu=0;yu<min(c[x],m+1);++yu){ l=1,r=0; for(reg p=0;p*c[x]+yu<=m;++p){ ++lp; // cout<<" infor "<<yu<<" "<<p<<" "<<l<<" "<<r<<endl; while(l<=r&&p-q[l]>d[x]) ++l; if(l<=r) f[x][p*c[x]+yu]=max(f[x][p*c[x]+yu],f[y][q[l]*c[x]+yu]+(p-q[l])*w[x]); while(l<=r&&f[y][q[r]*c[x]+yu]-q[r]*w[x]<=f[y][p*c[x]+yu]-p*w[x]) --r; q[++r]=p; } } // cout<<" bac x "<<x<<" lp "<<lp<<endl; } void dfs(int x,int fa){ dfn[x]=++df;fdfn[df]=x; sz[x]=1; for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(y==fa||vis[y]) continue; dfs(y,x); sz[x]+=sz[y]; } dfn2[x]=df; } void divi(int x){ rt=0; fin(x,0); df=0; dfs(rt,0); fdfn[df+1]=0; for(reg i=df;i>=1;--i) wrk(fdfn[i]); for(reg j=0;j<=m;++j){ ans=max(ans,f[rt][j]); } int tmp=rt; vis[tmp]=1; for(reg i=hd[tmp];i;i=e[i].nxt){ int y=e[i].to; if(vis[y]) continue; nowsz=sz[y]; divi(y); } } void clear(){ memset(hd,0,sizeof hd); cnt=0;df=0;ans=0; memset(vis,0,sizeof vis); } int main(){ int t;rd(t); while(t--){ clear(); rd(n);rd(m); for(reg i=1;i<=n;++i) rd(w[i]); for(reg i=1;i<=n;++i) rd(c[i]); for(reg i=1;i<=n;++i) rd(d[i]); int x,y; for(reg i=1;i<n;++i){ rd(x);rd(y);add(x,y);add(y,x); } nowsz=n; divi(1); printf("%d\n",ans); // prt(vis,1,n); // if(t==1)return 0; } return 0; } } signed main(){ freopen("data.in","r",stdin); freopen("my.out","w",stdout); Miracle::main(); return 0; } /* Author: *Miracle* Date: 2019/3/20 7:29:17 */