zoukankan      html  css  js  c++  java
  • [BZOJ4182] Shopping

    [BZOJ4182] Shopping

    Description

    马上就是小苗的生日了,为了给小苗准备礼物,小葱兴冲冲地来到了商店街。商店街有n个商店,并且它们之间的道路构成了一颗树的形状。第i个商店只卖第i种物品,小苗对于这种物品的喜爱度是wi,物品的价格为ci,物品的库存是di。但是商店街有一项奇怪的规定:如果在商店u,v买了东西,并且有一个商店w在u到v的路径上,那么必须要在商店w买东西。小葱身上有m元钱,他想要尽量让小苗开心,所以他希望最大化小苗对买到物品的喜爱度之和。这种小问题对于小葱来说当然不在话下,但是他的身边没有电脑,于是他打电话给同为OI选手的你,你能帮帮他吗?

    Input

    输入第一行一个正整数T,表示测试数据组数。对于每组数据,第一行两个正整数n;m;第二行n个非负整数w1,w2...wn;第三行n个正整数c1,c2...cn;第四行n个正整数d1,d2...dn;接下来n-1行每行两个正整数u;v表示u和v之间有一条道路

    Output

    输出共T 行,每行一个整数,表示最大的喜爱度之和。

    Sample Input

    1
    3 2
    1 2 3
    1 1 1
    1 2 1
    1 2
    1 3

    Sample Output

    4

    HINT

    N<=500,M<=4000,T<=5,Wi<=4000,Di<=100

    试题分析

    写了个暴力,然后某明奇妙地碾过去了。
    正解应该是点分治+单调队列优化多重背包。

    Part1 单调队列优化多重背包

    首先写出dp方程:(f[i][j]=max(f[i-1][j-v[i] imes k]+w[i] imes k,f[i][j]))
    考虑在将(j)表示成(pmod{v[i]})下的形式,设:(j=a imes v[i]+b)
    那么有:(f[i][a imes v[i]+b ]=max(f[i-1][(a-k) imes v[i]+b]+w[i] imes k,f[i][j]))
    还是没有突破口?继续换元,设:(t=a-k),有:
    (f[i][a imes v[i]+b]=max(f[i-1][t imes v[i]+b ]-t imes w[i]+a imes w[i],f[i][j]))
    再来讨论一下(t)的范围:
    (k)的范围:(0leq kleq min(mx[i],a)),其中(mx[i])表示第(i)件物品的上限。
    于是有(a-k)的范围:(max(0,a-mx[i]) leq t leq a),先枚举b,然后是a,t那维可以单调队列优化。
    时间复杂度:(O(nm))

    Part2 点分治

    发现这道题与在序列上多重背包的唯一区别就是在树上,也就是说我们只需要求出每一个点为根的连续购买的答案。
    当然,上面的单调队列包含不买的情况,这个在转移的时候不管就可以了。
    那么对于一棵树上的两个不同点来说,这两个点都可以代表这棵树,那么我们就不需要枚举每一个点了,只需要定一个顺序让一个点代表就可以了。
    一个点代表整棵树那就意味着当前这棵树上的所有点在以后都不能包含这个点了,然后我们需要让这棵树尽量平衡,自然而然使用点分治解决。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<vector>
    #include<algorithm>
    //#include<ctime>
    //#include<cmath>
    //#include<queue>
    
    using namespace std;
    #define LL long long
    
    inline int read(){
    	int x=0,f=1; char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    	for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    	return x*f;
    }
    const int INF = 2147483600;
    const int MAXN = 100010;
    
    int N,M; int cnt;
    int Next[MAXN<<1],Node[MAXN<<1],Root[MAXN<<1];
    int fa[MAXN+1]; bool vis[MAXN+1];
    int rt;//使用前清零
    int mx[MAXN+1],sz[MAXN+1];
    //f[0]记住为N+1
    int Sz;
    
    inline void insert(int u,int v){
    	Node[++cnt]=v; Next[cnt]=Root[u]; Root[u]=cnt; return ;
    }
    inline void Getroot(int k,int Fa){
    	mx[k]=0; sz[k]=1;
    	for(int x=Root[k];x;x=Next[x]){
    		int v=Node[x]; if(v==Fa||vis[v]) continue;
    		Getroot(v,k); sz[k]+=sz[v];
    		mx[k]=max(mx[k],sz[v]);
    	} mx[k]=max(mx[k],Sz-sz[k]); if(!rt||mx[rt]>mx[k]) rt=k;
    }
    int don[MAXN+1],nod[MAXN+1],r[MAXN+1];
    int w[MAXN+1],c[MAXN+1],d[MAXN+1];
    int tim; int dfn[MAXN+1];
    inline void Get_dfn(int k,int Fa){
    	dfn[k]=++tim; nod[tim]=k; sz[k]=1;
    	for(int x=Root[k];x;x=Next[x]){
    		int v=Node[x]; if(v==Fa||vis[v]) continue; Get_dfn(v,k); sz[k]+=sz[v];
    	} r[k]=tim;
    }
    int top; int sta[MAXN+1];
    int f[503][4001]; int ans=-INF;
    inline void dfs(int k){
    	vis[k]=true; tim=0; Get_dfn(k,-1); int head=1,tail=0;
    	//cout<<"true"<<endl;
    	//cout<<"end :"<<k<<endl;
    	//cout<<k<<" "<<tim<<endl;
    	memset(f[tim+1],0,sizeof(f[tim+1]));
    	//cout<<tim<<endl; int ne=0;
    	for(int i=tim;i>=1;i--){
    		int v=nod[i]; 
    		for(int j=0;j<=M;j++) f[i][j]=f[r[v]+1][j];
    		for(int j=0;j<c[v];j++){
    			head=1; tail=0;
    			for(int k=0;j+k*c[v]<=M;k++){
    				//++ne;
    				while(head<=tail&&sta[head]<k-d[v]) ++head;
    				if(head<=tail) f[i][j+k*c[v]]=max(f[i+1][sta[head]*c[v]+j]-(sta[head]*w[v])+k*w[v],f[i][j+k*c[v]]);
    				while(head<=tail&&f[i+1][j+k*c[v]]-(k*w[v])>=f[i+1][sta[tail]*c[v]+j]-(sta[tail]*w[v])) --tail;
    				sta[++tail]=k;
    			}
    		}
    	} for(int i=1;i<=M;i++) ans=max(ans,f[1][i]);
    	//cout<<"true"<<endl;
    	//cout<<ne<<endl;
    	for(int x=Root[k];x;x=Next[x]){
    		int v=Node[x]; if(vis[v]) continue;
    		Sz=sz[v]; rt=0;
    		Getroot(v,k); dfs(rt);
    	} return ;
    }
    
    int main(){
    	//freopen("a.in","r",stdin);
    	//freopen(".out","w",stdout);
    	int T=read();
    	while(T--){
    		ans=0; memset(Root,0,sizeof(Root)); cnt=0;
    		memset(f,0,sizeof(f)); 
    		memset(vis,0,sizeof(vis));
    		N=read(),M=read();
    		for(int i=1;i<=N;i++) w[i]=read();
    		for(int i=1;i<=N;i++) c[i]=read();
    		for(int i=1;i<=N;i++) d[i]=read();
    		for(int i=1;i<N;i++){
    			int u=read(),v=read();
    			insert(u,v); insert(v,u);
    		} mx[0]=N+1; Sz=N; rt=0; Getroot(1,0); //cout<<rt<<endl;
    		dfs(rt); printf("%d
    ",ans);
    	}
    	return 0;
    }
    
    
    
  • 相关阅读:
    01 React快速入门(一)——使用循环时对于‘key’报错处理
    01 div实现浮动效果
    17 制作热力图
    16 ArcGIS Server 10.6.1发布影像服务
    虚拟机上有关于Apache服务基于主机名@4域名访问网页
    虚拟机上有关于Apache服务基于IP地址@3IP访问网站
    Apache有关个人用户主页以及强制访问安全系统功能介绍@2
    Apache服务的安装以及服务文件参数内容的配置 @1
    WVware虚拟机linux环境下使用ssh服务以安全密钥的形式远程控制服务(本地客户端登录远程服务端)
    WVware虚拟机linux环境下RAID5 五块磁盘操作管理实例
  • 原文地址:https://www.cnblogs.com/wxjor/p/9682840.html
Copyright © 2011-2022 走看看