HDU

题目

传送门

给一棵 (n) 个点的树,每个点有体积 (a_i) 和价值 (b_i),求总容量为 ([1,m]) 时选出一个最大价值独立集(相邻节点不能同时选)的方案数。

(nle 50,mle 5000)

解法

( ext{dfs}) 序优化树形背包。

在讲这道题之前,先讲一下 另一道题

(f[i][j])( ext{dfs}) 序为 (i) 的点和 ( ext{dfs}) 序比它大的点选 (j) 门课的最大得分。则有:

[f[i][j]=max{f[i+siz[id[i]]][j],f[i+1][j-1]+s[id[i]]} ]

即跳过这棵子树和 可以 选择这棵子树。注意后面的情况可能会到另一棵子树,这就相当于选了 (i) 这个单点。

这样按 ( ext{dfs}) 序倒序转移即可。

好了回到这道题。

先考虑朴素 (mathtt{DP})。令 (f[i][j][0/1]) 为当前点 (i),已选 (j) 体积,是否选 (i) 的最大价值方案数,这是 (mathcal O(nm^2)) 的。

这里再提一句,有一种树形背包时间复杂度可以被证明是 (mathcal O(nm)) 的,不过这道题没有 (siz) 的限制,所以证明不成立。

我们可以用 ( ext{dfs}) 序优化。但很快发现一个问题,如下图(图源 ( ext{lhm_liu})):

(i ightarrow i+1) 的过程,你并不知道 (i+1) 是否可选。

思考为什么上面那道题可以这么做?上面那道题在父节点就可以判断是否可取(不取就整棵子树都不取),所以本身就可以随便算。而这道题取了只会导致儿子与父亲不可取,如果只考虑儿子显然是需要枚举的,这就破坏了复杂度。

那怎么办呢?首先可以想到再加一维状态表示是否选取,不过 (2^{50}) 显然挂了。

仔细思考,先令类似 (i+1) 的父亲的点为关键点。那么,对于 (u),它的最后一个遍历的子树不用添加 (u) 为关键点,因为 (u) 的子树之后已经遍历完了(很类似 ( ext{CodeForces - 600E Lomsat gelral}))。

那么我们重链剖分,将重链放到最后遍历。容易发现,点 (i) 包含的关键点就是 (i) 到根每条链的链顶,这样就是 (2^{log n}=n) 的。

单次复杂度 (mathcal O(n^2 m))

代码

#include <cstdio>

#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
#define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
#define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
#define print(x,y) write(x),putchar(y)

template <class T> inline T read(const T sample) {
    T x=0; int f=1; char s;
    while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
    while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
    return x*f;
}
template <class T> inline void write(const T x) {
    if(x<0) return (void) (putchar('-'),write(-x));
    if(x>9) write(x/10);
    putchar(x%10^48);
}
template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
template <class T> inline T fab(const T x) {return x>0?x:-x;}
template <class T> inline T gcd(const T x,const T y) {return y?gcd(y,x%y):x;}
template <class T> inline T lcm(const T x,const T y) {return x/gcd(x,y)*y;}
template <class T> inline T Swap(T &x,T &y) {x^=y^=x^=y;}

#include <vector>
#include <cstring>
using namespace std;
typedef long long ll;

const int maxn=55,maxm=5005;

int n,m,a[maxn],b[maxn],pos,siz[maxn],son[maxn],dfn[maxn],idx,f[maxn],tp[maxn],app[maxn];
vector <int> e[maxn],fa[maxn];
struct node {
	int val; ll cnt;
	
	node operator + (const int Val) {
		return (node){val+Val,cnt};
	}
	
	node operator + (const node t) {
		if(val<t.val) return t;
		else if(val==t.val) return (node){val,cnt+t.cnt};
		return *this;
	}
} g[2][maxn][maxm],ans[maxm];

void dfs1(int u,int fa) {
	siz[u]=1; f[u]=fa;
	for(int i=0;i<e[u].size();++i) {
		int v=e[u][i];
		if(v==fa) continue;
		dfs1(v,u);
		siz[u]+=siz[v];
		if(siz[son[u]]<siz[v]) son[u]=v;
	}
}

void dfs2(int u,int t) {
	tp[u]=t,dfn[++idx]=u;
	for(int i=0;i<e[u].size();++i) {
		int v=e[u][i];
		if(v==f[u] || v==son[u]) continue;
		dfs2(v,v);
	}
	if(son[u]) dfs2(son[u],t);
}

void init() {
	idx=0;
	memset(g,0,sizeof g);
	rep(i,1,n) 
		e[i].clear(),fa[i].clear(),
		son[i]=0;
	rep(i,1,m) ans[i]=(node){0,0};
}

int main() {
	int u,v,Father,S,Case=read(9),T=Case;
	for(;T;--T) {
		n=read(9),m=read(9); init();
		rep(i,1,n) a[i]=read(9),b[i]=read(9);
		rep(i,1,n-1) u=read(9),v=read(9),e[u].push_back(v),e[v].push_back(u);
		dfs1(1,0); dfs2(1,1);
		rep(i,1,n) {
			u=i;
			fa[i].push_back(i);
			while(f[tp[u]]) u=f[tp[u]],fa[i].push_back(u);
		}
		g[0][0][0]=(node){0,1};
		rep(i,1,n) {
			pos=i&1;
			rep(s,0,(1<<fa[dfn[i]].size())-1) rep(j,0,m) g[pos][s][j]=(node){0,0};
			Father=10;
			for(int j=0;j<fa[dfn[i-1]].size();++j) {
				u=fa[dfn[i-1]][j];
				if(u==f[dfn[i]]) Father=j;
				app[j]=-1;
				for(int k=0;k<fa[dfn[i]].size();++k)
					if(u==fa[dfn[i]][k]) app[j]=k;
			}
			rep(s,0,(1<<fa[dfn[i-1]].size())-1) {
				S=0;
				for(int j=0;j<fa[dfn[i-1]].size();++j)
					if((s>>j&1) && (~app[j])) S|=(1<<app[j]);
				rep(j,0,m) {
					if(!g[pos^1][s][j].cnt) continue;
					g[pos][S][j]=g[pos][S][j]+g[pos^1][s][j];
					if((s>>Father&1) || j+a[dfn[i]]>m) continue;
					g[pos][S|1][j+a[dfn[i]]]=g[pos][S|1][j+a[dfn[i]]]+(g[pos^1][s][j]+b[dfn[i]]);
				}
			}
		} 
		rep(i,0,(1<<fa[dfn[n]].size())-1) rep(j,0,m) ans[j]=ans[j]+g[pos][i][j];
		printf("Case %d:
",Case-T+1);
		rep(i,1,m) print(ans[i].cnt,i==m?'
':' ');
	}
	return 0;
}
原文地址:https://www.cnblogs.com/AWhiteWall/p/14432539.html