AGC007 E
来源:互联网 发布:c语言的符号函数 编辑:程序博客网 时间:2024/05/22 17:21
题意:
给出一棵n个节点树,除了叶节点,每个节点恰好有两个孩子。边上有边权。第一天根开始走,每天选一个叶节点,从当前点走到叶节点,最后一天走回根节点。要求每条边经过两次,每个叶节点被选一次。花费就是除了第一天和最后一天走的路程最远那一天的路程。问最小花费。
2< n< 131,072
#include<cstring>#include<cstdlib>#include<cstdio>#include<cmath>#include<iostream>#include<vector>#define N 150000#define LL long long#define pb push_backusing namespace std;struct node{int y,d,nex;}a[2*N];struct node1{LL a,b;}A[N],B[N];vector<node1> v[N];int n,fir[N],len,f[N],du[N],al,bl;LL ans,inf=1ll<<50,lim,g[N];void ins(int x,int y,int d){ a[++len].y=y;a[len].d=d;a[len].nex=fir[x];fir[x]=len;}void dfs(int x,int fa){ if(du[x]==1) {v[x].pb((node1){0,0});return;} int y1=0,y2=0,s1,s2,i1,i2;LL L,mi; for(int k=fir[x];k;k=a[k].nex) { int y=a[k].y; if(y==fa) continue; f[y]=a[k].d; dfs(y,x); if(y1==0) y1=y; else y2=y; } s1=v[y1].size();s2=v[y2].size(); if(s1==0 || s2==0) return; if(s1>s2) swap(s1,s2),swap(y1,y2); L=lim-f[y1]-f[y2]; al=bl=0; mi=inf;i2=0; for(i1=0;i1<s1;i1++) { while(v[y1][i1].b+mi>L && i2<s2) {mi=min(mi,v[y2][i2].a);i2++;} if(v[y1][i1].b+mi>L) break; A[++al]=(node1){v[y1][i1].a+f[y1],v[y2][i2-1].b+f[y2]}; } for(int i=0;i<s1;i++) g[i]=inf; mi=inf;i1=0; for(i2=0;i2<s2;i2++) { while(v[y2][i2].b+mi>L && i1<s1) {mi=min(mi,v[y1][i1].a);i1++;} if(v[y2][i2].b+mi>L) break; g[i1-1]=min(g[i1-1],v[y2][i2].a+f[y2]); } for(int i=0;i<s1;i++) if(g[i]!=inf) B[++bl]=(node1){g[i],v[y1][i].b+f[y1]}; i1=1;i2=1; while(i1<=al || i2<=bl) { node1 t; if(i2>bl) t=A[i1++]; else if(i1>al || A[i1].b>B[i2].b) t=B[i2++]; else t=A[i1++]; v[x].pb(t); }}bool check(){ for(int i=1;i<=n;i++) v[i].clear(); dfs(1,0); if(v[1].size()) return 1; return 0;}int main(){ LL l=0,r=0; scanf("%d",&n); for(int i=2;i<=n;i++) { int y,d;scanf("%d%d",&y,&d); ins(i,y,d);ins(y,i,d); du[i]++;du[y]++; r+=d; } while(l<=r) { LL mid=(l+r)/2; lim=mid; if(check()) ans=mid,r=mid-1; else l=mid+1; } printf("%lld\n",ans); return 0;}
题解:
膜了题解。。
先二分答案lim
考虑一个暴力做法,我们对每个节点x为根的子树,维护一堆的二元组(a,b)表示存在一种方案,第一天从x出发路程为a,最后一天回到x,路程为b。s[x]表示x的二元组数量。size[x]表示x子树大小。
设x的两个孩子是y1,y2,x到y1的边是t1,x到y2的边是t2。
对于y1的(a,b),y2的(x,y)
如果b+t1+x+t2<=lim,就可以合并为(a+t1,y+t2)。
如果y+t2+a+t1<=lim,就可以合并为(x+t2,b+t1)。
设s[y1]<=s[y2],那么我们可以对每个(a+t1,…)和(…,b+t1)找最优的另一部分,维护二元组一维有序双指针查找,然后归并就能线性完成。
这时,有s[x]<=2*s[y1]
由于总的运算量关于
而且显然s[x]<=size[x],那么这个复杂度就和每个节点遍历非最大子树相当,像cf那题一样分析就有
所以总复杂度
0 0
- AGC007 E
- e
- E
- E
- e
- e
- e
- e
- e
- E
- e
- E
- e
- e
- E
- e
- e
- e
- 更改jupyter-notebook的启动时的默认目录
- LeetCode-Easy部分中标签为HashTable的所有题目
- NSString使用之Copy和Strong
- POJ NOI MATH-7648 蓄水池水管问题
- 112. Path Sum
- AGC007 E
- 高德地图 公交站点查询
- 微信二次开发 新浪云应用SAE Token验证失败
- redis学习记录(三)-redis中的数据结构
- StrangeIoc MVCS游戏框架
- 浅谈WebLogic和Tomcat区别
- 一张图看明白云计算架构核心竞争力
- 解决 Successfully created project '' on GitHub, but initial push failed: Could not read from remote re
- laravel 环境搭建