[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]\times k]+w[i]\times k,f[i][j])\)
考虑在将\(j\)表示成\(\pmod{v[i]}\)下的形式,设:\(j=a\times v[i]+b\)
那么有:\(f[i][a\times v[i]+b ]=\max(f[i-1][(a-k)\times v[i]+b]+w[i]\times k,f[i][j])\)
还是没有突破口?继续换元,设:\(t=a-k\),有:
\(f[i][a\times v[i]+b]=\max(f[i-1][t\times v[i]+b ]-t\times w[i]+a\times w[i],f[i][j])\)
再来讨论一下\(t\)的范围:
\(k\)的范围:\(0\leq k\leq \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\n",ans);
	}
	return 0;
}


posted @ 2018-09-20 19:32  wxjor  阅读(172)  评论(0编辑  收藏