zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    mysql的用户权限设置
    Jmeter——BeanShell PreProcessor的用法
    zhlan--巧用Python中的正则表达式符号
    【直播预告】云栖直播:阿里热修复产品HotFix2.0升级详解
    双十一 手淘技术用了这几招
    用户说体验 | 关于阿里百川HotFix你需要了解的一些细节
    阿里百川码力APP监控 来了!
    淘宝直播技术分享:如何打造体验优秀的“直播+”产品?
    云栖大会上宣布即将开源的手淘Atlas什么来头?
    推进"五通一平":手淘技术"三大容器 五大方案"首次整体亮相 百川开放升级
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/14432539.html
Copyright © 2011-2022 走看看