【noip 2015】运输计划
来源:互联网 发布:生化危机解析知乎 编辑:程序博客网 时间:2024/05/18 00:00
去题面的传送门
题目的意思是:求将一棵树上的任意一条边权赋值为0时,所有航线的最长长度的最小值
想到二分答案
如何验证?
既然我们二分的答案是最长路线,也就是说,在将一条边权赋值为0之后,所有的路线长度应该都小于等于mid。但是只能删掉一条边,所以这条边是所有删边之前长度小于mid的路线的交边。问题转化为,能否找到一条边,被所有长度大于mid的路线经过。所以我们要统计每一条边被经过的次数。统计的方法便是树上差分了。需要把边的信息映射到点上面去,也就是把边的权值映射为儿子节点的点权。在差分时,路线两端点各+1,两点的lca-2,从子节点一直求前缀和,到根节点,便求出了每条边的经过次数。此外,在求前缀和时,如果每次都dfs一遍,可能会超时,但是父节点和子节点的序号大小是没有关系的,不好直接遍历。为了方便,我们求每一个点的dfs序,由于子节点的dfs序一定大于父亲节点,所以按照dfs序的大小直接for一遍求前缀和。最后,把所有点权for一遍,如果存在某一个经过次数恰好为长度大于mid的路径的条数,切它的权值大于等于各点权与mid的最大差值(也就是说,如果去掉它,一定保证所有的路线长度不超过mid),这时mid是合法的,二分左区间,找更小的答案。
以上是我自己打的解析。
———————————————-LXT的萌萌分割线—————————————————–
来看一下dalao的题解(写的还是蛮好的):
满分做法:
官方正解:
1.tarjan离线求lca+二分+树上差分;
2.树链剖分+二分+树上差分+dfs序;
Ps:树链剖分求lca的代码量比倍增小,并且效率高, 并且不难,建议大家学一下;
说一下第二种解法:
明确:
1.我们要删的边一定在最长路线上;
2.我们将边的信息映射到点上,也就是说儿子与父亲的边的信息,我们放在了儿子身上;
首先,最大值最小化,我们可以想到二分这个最大路线长度;
假设我们已经二分出一个mid:删边后最大路线的长度;
1.所有大于mid的路线都必须删去一条边,使之小于或等于mid;
2.总共只能删一条边;
联立1,2得:
我们要删的边出现在所有大于mid的线路上,删去这条边,原本最长的路线要等于mid,其他路线小于mid;
此时我们的mid是合法答案;
如何求一条边被所有路线经过的次数?
树上差分!
对于条路线,我们将它起点的边+1,终点的边+1,lca上面的边-2;
最后求一遍树上前缀和,就可以得到答案;
但是,如果我们每次都从根节点dfs一遍求前缀和,它的复杂度虽然是O(n),但递归会耗费大量时间,会被卡掉最后一个点;所以我们考虑用dfs序来求解;
在处理树上信息时顺便求出dfs序;
然后我们从后往前求前缀和就可以了,这样是没有后效性的,因为我们dfs时先访问父亲,再访问儿子,父亲的dfs序必然小于它儿子的dfs序,如果从后往前,必然先更新儿子,再更新父亲,复杂度是常数很小的O(n);
总复杂度:O(nlogn);
PS:这道题调试了很久,最后才发现挂在了lca上。求fa数组时,for循环的顺序一定不要搞错!因为父亲节点的序号和子节点的序号没有大小关系!如果把枚举节点序号放在外层循环,序号小的不一定是序号大的的父节点!
代码:
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>using namespace std;const int maxn=300000+10;int n,m,cnt,Index,l=-1,r,ans;int fist[maxn],nxt[maxn<<1],rank[maxn],deep[maxn],dfn[maxn];int fa[maxn][22],vpoint[maxn],cha[maxn];struct hh{ int f,t,v;}e[maxn<<1];struct lxt{ int f,t,len,lca;}ee[maxn];void build(int f,int t,int v){ e[++cnt]=(hh){f,t,v}; nxt[cnt]=fist[f]; fist[f]=cnt;}int make_lca(int x,int y){ if(deep[x]<deep[y]) swap(x,y); for(int i=log2(n);i>=0;--i) if(deep[fa[x][i]]>=deep[y]) x=fa[x][i]; if(x==y) return x; for(int i=log2(n);i>=0;--i) if(fa[x][i]!=fa[y][i]) { x=fa[x][i]; y=fa[y][i]; } return fa[x][0];}void dfs(int f,int t,int v){ fa[t][0]=f; deep[t]=deep[f]+1; rank[t]=v; dfn[++Index]=t; for(int i=fist[t];i!=-1;i=nxt[i]) if(e[i].t!=f) { vpoint[e[i].t]=e[i].v; dfs(t,e[i].t,v+e[i].v); }}void done(){ for(int i=1;i<=log2(n);++i) for(int j=1;j<=n;++j) fa[j][i]=fa[fa[j][i-1]][i-1];}bool check(int mid){ int maxx=0,tot=0; memset(cha,0,sizeof(cha)); for(int i=1;i<=m;++i) if(ee[i].len>mid) { tot++; maxx=max(maxx,ee[i].len-mid); cha[ee[i].f]++; cha[ee[i].t]++; cha[ee[i].lca]-=2; } if(!tot) return true; for(int i=n;i>=1;--i) cha[fa[dfn[i]][0]]+=cha[dfn[i]]; for(int i=2;i<=n;++i) if(cha[i]==tot&&vpoint[i]>=maxx) return true; return false;}int main(){ memset(fist,-1,sizeof(fist)); scanf("%d%d",&n,&m); for(int i=1;i<n;++i) { int a,b,t; scanf("%d%d%d",&a,&b,&t); r+=t; build(a,b,t); build(b,a,t); } dfs(0,1,0); done(); for(int i=1;i<=m;++i) { int f,t,len,lca; scanf("%d%d",&f,&t); lca=make_lca(f,t); len=rank[f]+rank[t]-2*rank[lca]; ee[i]=(lxt){f,t,len,lca}; } while(r-l>1) { int mid=(l+r)>>1; if(check(mid)) r=mid; else l=mid; } if(check(l)) ans=l; else ans=r; printf("%d",ans); return 0;}
- NOIP[2015] 运输计划
- NOIP 2015 运输计划
- 【noip 2015】运输计划
- NOIP 2015 运输计划
- [FT][???]NOIP 2015 运输计划
- 4632 NOIP[2015] 运输计划
- NOIp 2015 运输计划 LCA
- NOIP 2015 [D2 T3]运输计划
- NOIP 2015 && UOJ#150 运输计划
- 【BZOJ 4326】【NOIP 2015 d2t3】运输计划
- 2015NOIP运输计划倍增做法
- NOIP-2015 运输计划(被卡常)
- BZOJ_P4326[NOIP]2015 运输计划(LCA+Tarjan+二分)
- BZOJ 4326 运输计划 transport 【NOIP 2015】【树链剖分】
- UOJ 150|NOIP 2015 Day 2|运输计划|LCA
- [noip 2015] [codevs 4632] [bzoj 4326] 运输计划
- Luogu P2680 [NOIp提高组2015]运输计划
- (暴力55分+卡常95分补全版)NOIP 2015 D2 T3运输计划
- docker开发学习
- Android StudioEventBus配置遇到小问题
- 使用IDEA和Gradle构建Vertx项目
- Mysql 字符串like比较 大小写忽略
- c++类与编译器
- 【noip 2015】运输计划
- react native mac 下环境搭建
- 构造函数的执行过程与构造函数的规则,instanceof在构造函数中的作用
- learning R with swirl-missing value
- android面试-java内存模型
- JAVA虚拟机内存结构及分析
- 超人归来
- Intellij Idea 安装阿里巴巴java开发规约插件
- this指针