zoukankan      html  css  js  c++  java
  • P6326

    一个很不寻常的题啊。

    首先容易发现题目说的就是所有至少买了一个的点必须构成连通块。考虑在树上跑背包,那必须是有根的。随便设 (1)​ 为根,然后规定若 (x)​ 选了,则 (x)​ 的所有祖先都必须选,这样随后调用 (1)​ 的 DP 数组即可得到必须选 (1) 时的答案。设 (dp_{i,j}) 为子树 (i) 内花了不超过 (j) 元钱得到的最大收益,转移的时候将若干儿子树的 DP 数组合并起来,然后将 (i) 加入。这样你会发现,根本不需要令每个点为根跑一遍,因为一个连通块在有根树上的最浅点是唯一的,直接枚举所有点 (x) 作为最浅点,答案就是它的 (dp_{x,m}),max 起来即可。

    这个树形背包跟普通的复杂度可以分析到 (mathrm O(nm)) 的不同,这玩意是基于值域的,背包大小并没有和 size 取 min,每次合并是严格 (mathrm O!left(m^2 ight)) 的,总复杂度是 (mathrm O!left(nm^2 ight)),接受不了。注意到将 (i) 加入这个操作实际上是多重背包加入一个元素,可以用单调队列优化到 (mathrm O(m)),然并卵!毕竟复杂度瓶颈在于背包的「合并」。

    我们现在认为必须要根除背包的合并操作。此时引出一个科技:树上依赖性背包。问题就是说在有根树上,(x)​ 选了则必须选所有祖先的一个背包,无论是 01 还是完全还是多重。这玩意除了上述的暴力合并背包的 DP 做法,还有个比较妙的不带合并操作的 DP 做法。
    我们考虑像一个新的 DP 题一样从头开始考虑:一开始的局面是 (1sim n)​ 所有点未决策,现在考虑决策 (1)​。如果不选 (1)​ 那就直接结束走人吧!否则剩下 (2sim n)​ 需要决策,做完之后返回一个背包再将 (1)​ 插入。现在问题转化为 (2sim n)​。注意到这里的精髓:我们并没有将 (2sim n)​ 拆成若干儿子树再合并,而是直接当做一个整体!毕竟它本来就是一个整体,为啥要拆开再合并呢?(普通树形 DP 这么做是因为不会有「合并复杂度大,插入复杂度小」这样奇怪的事情发生,在这个前提下每个子树作为子问题显然是好理解的)
    现在假设 (2)​ 是 (1)​ 的儿子,对 (2)​ 进行决策。如果选的话那就 (3sim n)​ 走起;否则剩下 ({2sim n}-mathrm{subt}(2))​。对后者下一步应该选 (1)​ 的下一个儿子对吧这很自然。对前者呢?如果也选 (1)​ 的下一个儿子,那么得到的状态就是 (V)​​ 去掉一个儿子树再去掉一个儿子树的根???这状态就显得支离破碎,没法封闭。接下来官方给出的下一步决策的方案是这个算法的精髓:选 (3sim n)​ 中 dfn 最小的。就假设 (dfn_i=i)​ 吧,如果选的话转化到 (4sim n)​,否则去除 (mathrm{subt}(3))​,而注意到 (3)​ 子树里的点都大于 (3)​,而且是连续的,所以是 (3sim n)​ 的前缀,去掉之后得到 (1sim n)​ 的某个后缀。这样我们只要对每个状态都对 dfn 最小的点进行决策,转移到的状态就可以封闭在「dfn 后缀」这仅仅 (mathrm O(n))​​ 个状态里,而且每次转移都是加入一个点的操作。

    通过上述科技,我们可以用单调队列在 (mathrm O(nm)) 时间内计算出连通块必须包含 (1) 的答案。但这样一个坏处是不能顺便得到连通块最浅节点为其它点的答案了。那难道要对每个点都跑一遍吗?这样是 (mathrm O!left(n^2m ight)),虽然比原来的做法优,但还是过不去。不难发现,考虑过包含 (1) 的情况,剩下不包含 (1) 的情况,这样必须在 (1) 的若干个儿子树的其中之一,就转化为了子问题,递归下去。可以用点分治来限制层数,达到 (mathrm O(nmlog n))。这里点分治不再是统计路径,而是统计连通块,也很自然对吧?

    code(好写!)
    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    #define pb push_back
    const int inf=0x3f3f3f3f3f3f3f3f;
    const int N=510,M=4010;
    int n,m;
    int v[N],w[N],lim[N];
    vector<int> nei[N];
    bool vis[N];
    int dfn[N],mxdfn[N],nowdfn,mng[N];
    void dfs(int x,int fa=0){
    	mng[dfn[x]=mxdfn[x]=++nowdfn]=x;
    	for(int i=0;i<nei[x].size();i++){
    		int y=nei[x][i];if(y==fa||vis[y])continue;
    		dfs(y,x);
    		mxdfn[x]=mxdfn[y];
    	}
    }
    int dp[N][M];
    int q[M],head,tail;
    int ans;
    int sz[N],mxsz[N];
    bool cmp(int x,int y){return mxsz[x]<mxsz[y];}
    int gtrt(int x=1,int tot=n,int fa=0){
    	sz[x]=1,mxsz[x]=0;
    	int rt=0;
    	for(int i=0;i<nei[x].size();i++){
    		int y=nei[x][i];if(y==fa||vis[y])continue;
    		rt=min(rt,gtrt(y,tot,x),cmp);
    		sz[x]+=sz[y],mxsz[x]=max(mxsz[x],sz[y]);
    	}
    	mxsz[x]=max(mxsz[x],tot-sz[x]);
    	return min(rt,x,cmp);
    }
    void cdq(int x){
    	nowdfn=0,dfs(x);
    	memset(dp[nowdfn+1],0,sizeof(dp[0]));
    	for(int i=nowdfn;i;i--){
    		int u=mng[i];
    		for(int j=0;j<=m;j++)dp[i][j]=dp[mxdfn[u]+1][j];
    		for(int j=0;j<w[u]&&j<=m;j++){
    			head=tail=0;
    			for(int k=j;k<=m;k+=w[u]){
    				if(k>=w[u]){
    					while(head<tail&&dp[i+1][q[tail-1]]-q[tail-1]/w[u]*v[u]<=dp[i+1][k-w[u]]-(k-w[u])/w[u]*v[u])tail--;
    					q[tail++]=k-w[u];
    				}
    				while(head<tail&&(k-q[head])/w[u]>lim[u])head++;
    				if(head<tail)dp[i][k]=max(dp[i][k],dp[i+1][q[head]]+(k-q[head])/w[u]*v[u]);
    			}
    		}
    	}
    	ans=max(ans,dp[1][m]);
    	vis[x]=true;
    	for(int i=0;i<nei[x].size();i++){
    		int y=nei[x][i];if(vis[y])continue;
    		cdq(gtrt(y,sz[y]));
    	}
    }
    void mian(){
    	cin>>n>>m;
    	for(int i=1;i<=n;i++)nei[i].clear();
    	for(int i=1;i<=n;i++)scanf("%lld",v+i);
    	for(int i=1;i<=n;i++)scanf("%lld",w+i);
    	for(int i=1;i<=n;i++)scanf("%lld",lim+i);
    	for(int i=1;i<n;i++){
    		int x,y;
    		scanf("%lld%lld",&x,&y);
    		nei[x].pb(y),nei[y].pb(x);
    	}
    	memset(vis,0,sizeof(vis));
    	ans=0,cdq(gtrt());
    	cout<<ans<<"
    ";
    }
    signed main(){mxsz[0]=inf;
    	int t;
    	cin>>t;
    	while(t--)mian();
    	return 0;
    }
    
    珍爱生命,远离抄袭!
  • 相关阅读:
    IE下PNG透明图片fadeIn出现黑边的问题
    愿闻其翔记(一)
    简单的日期选择器
    HTML5 贪吃蛇
    HTML5小程序,变化的色彩
    HTML5 Canvas 基本图形画法
    帝国CMS实现一二级导航及其高亮
    php中json_decode()和json_encode()
    JavaScript重复元素处理
    JQuery在光标位置插入内容
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/solution-p6326.html
Copyright © 2011-2022 走看看