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
*/

 

posted @ 2019-03-20 10:21  *Miracle*  阅读(283)  评论(0编辑  收藏  举报