• 最佳团体[JSOI2016]


    https://www.luogu.com.cn/problem/P4322

    题解

    网上怎么这么多 (O(n^3log n)) 假做法啊。。。一条链就卡掉了

    假设0号节点是根 建出表示依赖关系的图 发现正好有 (n) 条边且每个点的父亲都比它编号小所以正好是一棵树

    那么原问题就是要在树上取一个包含根的性价比最高的连通块

    看到"性价比"考虑使用01分数规划 假设 (x_iin {0,1}) 表示第 (i) 个人选不选 答案是 (ans) 那么

    (dfrac{sum x_iP_i}{sum x_iS_i} le ans)

    挪一下

    (sum x_i(P_i-S_i*ans) le 0)

    二分答案 (mid) ,如果上方左式可以大于0说明答案小了 否则答案大了

    如何求出左式的最大值?

    考虑dp,求出原树的dfs序,设 (f[i][j]) 表示考虑到dfs序为 (i) 的点,共选了 (j) 个点

    (V_i=P_i-S_i*mid)(a_i) 是dfs序为 (i) 的点的 (V)

    可以选择这个点并且进入它的子树,即

    (f[i+1][j] = max(f[i+1][j], f[i][j-1]+a_i))

    可以不选这个点,这样它子树里的所有点都不能选了,所以直接跳到下一个在它子树外的点,假设它的dfs序是 (k)

    (f[k][j]=max(f[k][j],f[i][j]))

    这种按dfs序dp的方式常见于求包含根的连通块

    先预处理一下每个点的dfs序和下一个它子树外的点的dfs序编号

    一次dp是 (O(n^2)) 的,总时间 (O(n^2log n))

    #include <bits/stdc++.h>
    #define N 2505
    using namespace std;
    
    template <typename T>
    inline void read(T &num) {
    	T x = 0, ff = 1; char ch = getchar();
    	for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') ff = -1;
    	for (; ch <= '9' && ch >= '0'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ '0');
    	num = x * ff; 
    }
    
    int m, n, a[N], b[N], dfn[N], ed[N], tme = -1;
    int head[N], pre[N<<1], to[N<<1], sz;
    double p[N], f[N][N], ans;
    
    inline void addedge(int u, int v) {
    	pre[++sz] = head[u]; head[u] = sz; to[sz] = v;
    	pre[++sz] = head[v]; head[v] = sz; to[sz] = u;
    }
    
    void dfs(int x, int fa) {
    	dfn[x] = ++tme;
    	for (int i = head[x]; i; i = pre[i]) {
    		int y = to[i]; if (y == fa) continue; dfs(y, x); 
    	}
    	ed[dfn[x]] = tme;
    }
    
    double check(double k) {
    	for (int i = 0; i <= n+1; i++) {
    		p[dfn[i]] = 1.0 * a[i] - k * b[i];
    		for (int j = 0; j <= m; j++) f[i][j] = -1e9;
    	} 
    	f[0][0] = 0; 
    	for (int i = 0; i <= n; i++) {
    		for (int j = 0; j <= m; j++) {
    			f[ed[i]+1][j] = max(f[ed[i]+1][j], f[i][j]);
    			if (j > 0) f[i+1][j] = max(f[i+1][j], f[i][j-1] + p[i]);
    		} 
    	}  
    	return f[n+1][m];
    } 
    
    int main() {
    	read(m); read(n); ++m;
    	for(int i = 1, x; i <= n; i++) {
    		read(b[i]); read(a[i]); read(x);
    		addedge(x, i);
    	}
    	dfs(0, 0);
    	double l = 0, r = 1e4, mid = 0;
    	while (r - l > 1e-5) {
    		mid = (l + r) / 2;
    		if (check(mid) > 0) {
    			l = mid + 1e-7;
    		} else r = mid - 1e-7;
    	}
    	printf("%.3lf
    ", l);
    	return 0;
    }
    
  • 相关阅读:
    linux系统中ssh部署两台服务器远程免密登录
    R语言绘图常用的颜色组合
    R语言中将矩阵转化为行列的形式
    python中返回列表中元素的索引
    R语言如何读取excel数据
    linux系统中实现网络会话共享功能
    python中求1到100的和(循环变量的和)
    linux系统中创建网络会话
    Java程序的三十个基本规则
    风雨20年:我所积累的20条编程经验
  • 原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM119.html
Copyright © 2020-2023  润新知