zoukankan      html  css  js  c++  java
  • luogu P4322 [JSOI2016]最佳团体

    LINK:最佳团体

    挂这道题是有原因的 get到了几种树上背包的做法。

    显然是一道分数规划的问题 二分一下跑一个树上背包 (n^2log) (稳过。

    考虑树上背包的三种姿势。

    1. 暴力树上合并 我们以儿子的大小为合并的终点 这样做 复杂度被证明为n^2.

    关于子树大小之类的证明 我不太清楚 但是还有一种证明是考虑一对点对 只有在LCA和并时才会带来贡献 所以n^2个点对 所以复杂度n^2.(整个证明容易理解的多。

    1. 树上背包的可泛化 这个是我一直以来用的方法 专门针对于树上背包的问题。就是我们去掉合并背包的过程 dfs某个子节点的时候把父亲的背包直接传给儿子。

    可以证明 我们实现了可泛化这个问题。容易发现这个方法是正确的。

    1. 树上背包转dfs序上背包 这个也比较有意思。

    做法:对于树上每个节点我们搞出来一个dfs序 然后再求出nex数组 nex[x]表示x这个dfs序后外面的第一颗子树的dfs序.

    设f[i][j]表示dfs序为i此时选了j个点的最大价值 显然有转移 一棵子树内dfs序时连续的 如果当前选这个点那么就有资格选其子树内的点.

    f[i+1][j+1]=max{f[i][j]+v[i]};

    当然可以不选这个点那么也没有资格选这个子树内的点 必须得跳到下一棵子树内 所以此时有nex数组。

    f[nex[i]][j]=max{f[i][j]};

    可以发现 我们把dfs的过程变成了bfs的过程 从依赖型背包变到了普通背包 这就是差别。

    const int MAXN=2510;
    int m,n,len;db maxx;
    int a[MAXN],b[MAXN];
    int lin[MAXN],ver[MAXN],nex[MAXN];
    db f[MAXN][MAXN],g[MAXN];
    inline void add(int x,int y)
    {
    	ver[++len]=y;
    	nex[len]=lin[x];
    	lin[x]=len;
    }
    inline void dfs(int x)
    {
    	go(x)
    	{
    		for(int j=1;j<=m;++j)f[tn][j]=f[x][j];
    		dfs(tn);
    		for(int j=m;j>=1;--j)f[x][j]=max(f[x][j],f[tn][j-1]+g[tn]);
    	}
    }
    inline int check(db w)
    {
    	rep(0,n,i)
    	{
    		g[i]=b[i]-w*a[i];
    		rep(1,m,j)f[i][j]=-INF;
    	}
    	dfs(0);
    	return f[0][m]>=0;
    }
    int main()
    {
    	freopen("1.in","r",stdin);
    	get(m);get(n);
    	rep(1,n,i)
    	{
    		get(a[i]);get(b[i]);
    		add(read(),i);
    		maxx=max(maxx,(db)b[i]/a[i]);
    	}
    	db l=0,r=maxx;
    	while(l+EPS<r)
    	{
    		db mid=(l+r)/2;
    		if(check(mid))l=mid;
    		else r=mid;
    	}
    	printf("%.3lf",l);
    	return 0;
    }
    
  • 相关阅读:
    给窗体加个圣诞帽——抛砖引玉
    《高手寂寞》随感
    离职日记-计划与变化
    什么样的生活
    这一年……
    写在2011第一天的工作前
    Visual C++ 学习笔记四 —— 模板
    bugfree安装与配置
    QTP环境变量的使用
    测试提问单[转]
  • 原文地址:https://www.cnblogs.com/chdy/p/12561005.html
Copyright © 2011-2022 走看看