zoukankan      html  css  js  c++  java
  • [BZOJ4726][POI2017]Sabota?

    [BZOJ4726][POI2017]Sabota?

    试题描述

    某个公司有n个人, 上下级关系构成了一个有根树。其中有个人是叛徒(这个人不知道是谁)。对于一个人, 如果他下属(直接或者间接, 不包括他自己)中叛徒占的比例超过x,那么这个人也会变成叛徒,并且他的所有下属都会变成叛徒。你要求出一个最小的x,使得最坏情况下,叛徒的个数不会超过k。

    输入

    第一行包含两个正整数n,k(1<=k<=n<=500000)。
    接下来n-1行,第i行包含一个正整数p[i+1],表示i+1的父亲是p[i+1](1<=p[i+1]<=i)。

    输出

    输出一行一个实数x,误差在10^-6以内都被认为是正确的。

    输入示例

    9 3
    1
    1
    2
    2
    2
    3
    7
    3

    输出示例

    0.6666666667

    数据规模及约定

    见“输入

    题解

    正解是一个非常神的树形 dp,贴传送门,讲的挺好的。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 500010
    #define maxm 1000010
    
    int n, K, m, head[maxn], nxt[maxm], to[maxm], fa[maxn], siz[maxn];
    double f[maxn];
    
    void AddEdge(int a, int b) {
    	to[++m] = b; nxt[m] = head[a]; head[a] = m;
    	swap(a, b);
    	to[++m] = b; nxt[m] = head[a]; head[a] = m;
    	return ;
    }
    void build(int u) {
    	siz[u] = 1;
    	for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa[u])
    		build(to[e]), siz[u] += siz[to[e]];
    	return ;
    }
    void dp(int u) {
    	f[u] = 0;
    	if(siz[u] == 1) f[u] = 1;
    	for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa[u]) {
    		dp(to[e]);
    		f[u] = max(f[u], min(f[to[e]], (double)siz[to[e]] / (siz[u] - 1)));
    	}
    	return ;
    }
    
    int main() {
    	n = read(); K = read();
    	for(int i = 2; i <= n; i++) fa[i] = read(), AddEdge(i, fa[i]);
    	
    	build(1);
    	dp(1);
    	
    	double ans = 0;
    	for(int i = 1; i <= n; i++) if(siz[i] > K) ans = max(ans, f[i]);
    	printf("%lf
    ", ans);
    	
    	return 0;
    }
    

    当然这题也可以二分 + 树形 dp 做:对于二分的答案 x,我们算出 f[i],表示节点 i 为根的子树中在 x 的比例条件下最坏有几个人叛变,转移显然。

    不得不吐槽的是这样做得卡常数。当然这题非常妙,保证 n 到 1 是自下而上的顺序,所以树形 dp 或是树上预处理就可以不用递归了。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 500010
    
    int n, K, m, fa[maxn], siz[maxn], f[maxn];
    void build() {
    	for(int i = 1; i <= n; i++) siz[i] = 1;
    	for(int i = n; i > 1; i--) siz[fa[i]] += siz[i];
    	return ;
    }
    bool check(double x) {
    	for(int i = 1; i <= n; i++) f[i] = 1;
    	for(int i = n; i > 1; i--) {
    		f[fa[i]] = max(f[fa[i]], f[i]);
    		if(f[i] > x * (siz[fa[i]] - 1)) f[fa[i]] = siz[fa[i]];
    	}
    	return f[1] <= K;
    }
    
    int main() {
    	n = read(); K = read();
    	for(int i = 2; i <= n; i++) fa[i] = read();
    	
    	build();
    	double l = 0, r = 1;
    	while(r - l > 1e-7) {
    		double mid = (l + r) * 0.5;
    		if(check(mid)) r = mid; else l = mid;
    	}
    	printf("%lf
    ", l);
    	
    	return 0;
    }
    

    非递归真的好快。。。运行时间大概是递归版的 15.79%。。。

  • 相关阅读:
    第四次作业—— 分析比较各种软件构建环境
    如何实现点击事件触发之后刷新还保存原值
    简单理解js闭包
    javascript中 __proto__与prorotype的理解
    原生和jQuery的ajax用法
    getElementById和querySelector方法的区别
    关于javascript闭包理解
    第二篇 进销存管理系统冲刺博客
    个人项目:WC
    自我介绍+软工五问
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/6502288.html
Copyright © 2011-2022 走看看