zoukankan      html  css  js  c++  java
  • BZOJ 4182 Shopping (点分治+树上多重背包)

    题目大意:给你一颗树,你有$m$元钱,每个节点都有一种物品,价值为$w$,代价为$c$,有$d$个,如果在$u$和$v$两个城市都购买了至少一个物品,那么$u,v$路径上每个节点也都必须买至少一个物品

    单调队列数组开小了调了2h

    通过这道题,本蒟蒻终于$get$到了树上带权背包的正确姿势

    合并背包的代价是$O(m^{2})$的,非常不友好,而在序列上处理背包时,是不需要合并背包的,所以我们把树拍成$dfs$序

    显然,树上背包需要用子节点更新父节点的信息,所以倒序枚举时间戳$i$

    设$f[i][j]$表示时间戳为$i$,总代价为$j$时,所有时间戳$>=i$的节点,树上背包能得到的最大价值,令$x$表示时间戳为$i$的节点编号

    如果不选节点$x$,那么它子树内的节点都不能选,为了保证$f[i]$是$i$后面所有节点构成的最优解,所以跳过$x$子树的状态,用$f[ed_{x}+1]$更新$f[i]$,$ed_{x}$表示节点$x$的出栈时间

    如果选节点$x$,那么可以选$x$的子树内的节点,用$f[i+1]$更新$f[i]$

    两者取最优解即可

    树上带权$01$背包的时间被我们优化成了$O(nm)$

    多重背包可以用单调队列优化,时间一样是$O(nm)$

    而上面的$dp$方程仅适用于必须链并经过根节点的情况

    所以使用点分治每次选择一个重心作为根跑$DP$

    总时间$O(Tnmlogn)$

    代码巨丑

      1 #include <cmath>
      2 #include <vector>
      3 #include <cstdio>
      4 #include <cstring>
      5 #include <algorithm>
      6 #define N1 510
      7 #define M1 4010
      8 #define ll long long
      9 #define inf 233333333
     10 using namespace std;
     11 
     12 int gint()
     13 {
     14     int ret=0,fh=1;char c=getchar();
     15     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
     16     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
     17     return ret*fh;
     18 }
     19 int n,K,T;
     20 struct Edge{
     21 int to[M1],nxt[M1],head[N1],cte;
     22 void ae(int u,int v)
     23 {cte++;to[cte]=v,nxt[cte]=head[u],head[u]=cte;}
     24 }E;
     25 
     26 int W[N1],C[N1],D[N1];
     27 int st[N1],ed[N1],id[N1],tot;
     28 int sz[N1],lim[N1];
     29 int use[N1],mi,G;
     30 void gra(int u,int dad,int szfa)
     31 {
     32     int j,v,ma=szfa;
     33     if(szfa>mi) return;
     34     for(j=E.head[u];j;j=E.nxt[j])
     35     {
     36         v=E.to[j]; if(use[v]||v==dad) continue;
     37         ma=max(ma,sz[v]);
     38         gra(v,u,szfa+sz[u]-sz[v]);
     39     }
     40     if(ma<mi) mi=ma,G=u;
     41 }
     42 void dfs_pre(int u,int dad)
     43 {
     44     int j,v; sz[u]=0;
     45     st[u]=++tot; id[tot]=u;
     46     for(j=E.head[u];j;j=E.nxt[j])
     47     {
     48         v=E.to[j]; if(use[v]||v==dad) continue;
     49         lim[v]=lim[u]-C[u]; 
     50         dfs_pre(v,u); sz[u]+=sz[v];
     51     }
     52     sz[u]++; ed[u]=tot;
     53 }
     54 int que[M1],hd,tl;
     55 int f[N1][M1],ans,de;
     56 void calc(int u)
     57 {
     58     int i,j,k,p,x,w,d,c;
     59     memset(f[tot+1],0,sizeof(f[tot+1]));
     60     for(i=tot;i;i--)
     61     {
     62         x=id[i]; c=C[x]; d=D[x]; w=W[x]; 
     63         memcpy(f[i],f[ed[x]+1],sizeof(f[i]));
     64         for(j=0;j<c;j++)
     65         {
     66             hd=1,tl=0,que[++tl]=0;
     67             for(k=1;k*c+j<=lim[x];k++)
     68             {
     69                 while(hd<=tl&&k-que[hd]>d)
     70                     hd++;
     71                 f[i][k*c+j]=max(f[i][k*c+j],f[i+1][que[hd]*c+j]+(k-que[hd])*w);
     72                 while(hd<=tl&&f[i+1][k*c+j]-k*w>=f[i+1][que[tl]*c+j]-que[tl]*w)
     73                     tl--;
     74                 que[++tl]=k;
     75             }
     76         }
     77     }
     78     for(j=0;j<=K;j++) ans=max(ans,f[1][j]);
     79 }
     80 void main_dfs(int u)
     81 {
     82     int j,v;
     83     use[u]=1; tot=0,lim[u]=K;
     84     dfs_pre(u,-1);
     85     calc(u);
     86     for(j=E.head[u];j;j=E.nxt[j])
     87     {
     88         v=E.to[j]; if(use[v]) continue;
     89         mi=inf,G=0,gra(v,u,0);
     90         main_dfs(G);
     91     }
     92 }
     93 void MAIN()
     94 {
     95     dfs_pre(1,-1); 
     96     mi=inf,G=0,gra(1,-1,0);
     97     main_dfs(G);
     98 }
     99 void init()
    100 {
    101     tot=0,E.cte=0,ans=0;
    102     memset(use,0,sizeof(use));
    103     memset(E.head,0,sizeof(E.head));
    104 }
    105 
    106 int main()
    107 {
    108     freopen("t2.in","r",stdin);
    109     scanf("%d",&T);
    110     while(T--)
    111     {
    112         scanf("%d%d",&n,&K);
    113         int i,x,y,z; init();
    114         for(i=1;i<=n;i++) W[i]=gint();
    115         for(i=1;i<=n;i++) C[i]=gint();
    116         for(i=1;i<=n;i++) D[i]=gint();
    117         for(i=1;i<n;i++)
    118         {
    119             x=gint(), y=gint();
    120             E.ae(x,y),E.ae(y,x);
    121         }
    122         MAIN();
    123         printf("%d
    ",ans);
    124     }
    125     return 0;
    126 }
  • 相关阅读:
    图片热点 网页划区 网页的拼接 表单
    html body的属性 格式控制标签 内容容器标签 超链接标签 图片标签 表格
    结构体
    out 传值
    c#数组,手机号随机数抽奖
    输入月份和日期,输出是今年的第多少天,利用switch和case
    c#,for穷举,百鸡百钱
    c#条件运算符的使用,判断时间是上午还是下午
    c#关于try catch finally的使用,判断日期格式是否正确
    c#数组,例题
  • 原文地址:https://www.cnblogs.com/guapisolo/p/10180388.html
Copyright © 2011-2022 走看看