bzoj1758+WC2010
来源:互联网 发布:lovelive淘宝推荐 编辑:程序博客网 时间:2024/06/10 19:20
题目大意,给出一棵树,有边权,找出其中一条包含了不少于L,不多于R条边的路径,使得 Average(v(e)) 最大,上式表示所有选择的边的平均权值。
(n<=50000,边权<=10^7)
思路:因为是平均数所以想到了二分(其实根本想不到),我们二分最后的答案,将树上所有边都减去当前二分到的的平均数,然后再在树中找有没有一条包含了不少于L,不多于R条边的路径,使得路径总长>=0了,如果有,说明当前答案小了,否则当前答案大了。
对于二分答案的的验证,我们采用点分治,对于每一个当前子树(设为s),我们设G(s)为当前子树的重心,son(s)为儿子的集合,那么对于每一个状态s,我们都必须计算出以s根为转折点的最长路径(当然要满足限制,之后不赘述了),那么我们这样考虑,对于当前s,我们找到它的重心,设为当前的root,那么这样以后树的深度就不会超过log层了,我们对于当前的每个son(root),找到它到根的很多条链,我们设g(k)为son中,包含了k条边的最长的链长度,然后合并到当前root的状态上。(合并的过程用单调队列实现)。
那么复杂度为什么是n*log的呢?我们想到对于深度为1的root,我们需要进行o(n)的操作(包括找重心,找链,单调队列合并(虽然常数大但是确实是o(n)的)),那么第二层一共也最多加起来进行o(n)的操作,因为一共log层,所以不超过n*log的,常熟巨大就是了。
#include <iostream>#include <cstring>#include <cstdlib>#include <string>#include <cstdio>#include <algorithm>#include <cmath>#include <ctime>using namespace std;struct node{int to;int next;double len;};node bian[400010];int sum = 0,first[400010],size[400010],leng,lenf,max_weight,l,r;int weight,q[400010],n,a,b;double Mina,g[400010],f[400010],c;bool visit[400010],found;void inser(int x,int y,double z) { bian[ ++ sum].to = y; bian[sum].next = first[x]; bian[sum].len = z; first[x] = sum;}void dfs(int x,int Anc) {size[x] = 1;for(int u = first[x];u;u = bian[u].next) if(!visit[bian[u].to] && bian[u].to != Anc) { dfs(bian[u].to,x); size[x] += size[bian[u].to]; }return;}void bfs(int x,int Anc,int depth,double val) {if(depth > leng) leng = depth,g[leng] = -1000000000000;if(val > g[depth]) g[depth] = val;for(int u = first[x];u;u = bian[u].next) if(!visit[bian[u].to] && bian[u].to != Anc) bfs(bian[u].to,x,depth + 1,val + bian[u].len - Mina);}void find_weight(int x,int Anc,int F) {int ret = 0;int sum = 0;for(int u = first[x];u;u = bian[u].next) if(!visit[bian[u].to] && bian[u].to != Anc) { find_weight(bian[u].to,x,F); if(size[bian[u].to] > ret) ret = size[bian[u].to]; sum += size[bian[u].to]; }if(size[F] - sum > ret) ret = size[F] - sum;if(ret < max_weight) weight = x,max_weight = ret;} void Div(int x,int Anc) {dfs(x,Anc);if(l > size[x]) return ;max_weight = 10000000;find_weight(x,Anc,x);dfs(weight,weight); for(int i = 0;i <= size[weight];i ++) f[i] = g[i] = -1000000000000; lenf = 0; for(int u = first[weight];u;u = bian[u].next) if(!visit[bian[u].to] && bian[u].to != Anc) { leng = 0; bfs(bian[u].to,weight,1,bian[u].len - Mina); int head = 1,tail = 0,last = leng; for(int i = 0;i <= lenf;i ++) { while(last + i >= l && last >= 0) { while(head <= tail && q[head] + i > r) head ++; while(head <= tail && g[last] > g[q[tail]]) tail --; q[ ++ tail] = last; last --; }if(head <= tail && g[q[head]] + f[i] > 0) { found = true;return ;} } lenf = max(lenf,leng); for(int i = 0;i <= leng;i ++) if(g[i] > f[i]) f[i] = g[i]; } visit[weight] = true; for(int u = first[weight];u;u = bian[u].next) if(!visit[bian[u].to] && bian[u].to != Anc && found == false) Div(bian[u].to,weight);}bool check(double x) {memset(visit,false,sizeof(visit));Mina = x;found = false;Div(1,0);return found;}int main() {scanf("%d",&n);scanf("%d%d",&l,&r);for(int i = 1;i < n;i ++) {scanf("%d%d",&a,&b);scanf("%lf",&c);inser(a,b,c);inser(b,a,c);}double head = 0,tail = 1000000.0;while(tail - head > 0.0001){double Mid = (head + tail) / 2.0;if(check(Mid)) head = Mid;else tail = Mid;}printf("%0.3f",head);return 0;}
0 0
- bzoj1758+WC2010
- BZOJ1758 [WC2010]重建计划
- bzoj1758 [Wc2010]重建计划
- [BZOJ1758] [WC2010] 重建计划 - 树分治
- 【bzoj1758】[Wc2010]重建计划 二分答案+单调队列+点分治
- 【BZOJ1758】【Wc2010】重建计划 树的点分治 二分
- wc2010 bzoj1758(点分治+二分+单调队列) TLE
- 树的点分治 bzoj1758【WC2010】重建计划
- BZOJ1758 [Wc2010]重建计划(二分答案+点分治+单调队列)
- 【BZOJ1758】【Wc2010】重建计划 分数规划+树分治单调队列check
- WC2010 排序机
- 1758: [Wc2010]重建计划
- 1758: [Wc2010]重建计划
- 【BZOJ 1758】 [Wc2010]重建计划
- bzoj 1758: [Wc2010]重建计划
- [bzoj 1758] [Wc2010]重建计划
- BZOJ1758【点分治】【二分】【单调队列】
- 【BZOJ1758】重建计划,点分治+单调队列
- python random.shuffle(随机打乱列表等) 和 random.random
- Android开源项目总结
- SVM介绍
- LeetCode Climbing Stairs
- this指针的理解
- bzoj1758+WC2010
- 2013百度校招运维dba笔试+三面,社招一面
- 层序遍历二叉树-测试
- 操作系统虚拟化解决方案
- Android Studio 快捷键
- iOS开发-Socket编程
- AVD启动后出现 "Storage space running out"
- IntelliJ IDEA和Eclipse最常用的快捷键对应表:
- Android SDK Manage 代理设置