【NOIP2015】运输计划 {二分答案+倍增+树上差分}
2016-10-24 20:40
381 查看
【题目描述】
公元 2044 年,人类进入了宇宙纪元。
L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球。
小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之间不会产生任何干扰。
为了鼓励科技创新, L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。
在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后,这 m 个运输计划会同时开始,所有飞船一起出发。当这 m 个运输计划都完成时,小 P 的物流公司的阶段性工作就完成了。
如果小 P 可以自由选择将哪一条航道改造成虫洞, 试求出小 P 的物流公司完成阶段性工作所需要的最短时间是多少?
【Sample Input】
(第一行包括两个正整数 n,m,表示 L 国中星球的数量及小 P 公司预接的运输计划的数量,星球从 1 到 n 编号。
接下来 n−1 行描述航道的建设情况,其中第 i 行包含三个整数 ai,bi 和 ti,表示第 i 条双向航道修建在 ai 与 bi 两个星球之间,任意飞船驶过它所花费的时间为 ti。数据保证 1≤ai,bi≤n 且 0≤ti≤1000。
接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 uj 和 vj,表示第 j 个运输计划是从 uj 号星球飞往 vj号星球。数据保证 1≤ui,vi≤n)
6 3
1 2 3
1 6 4
3 1 7
4 3 6
3 5 5
3 6
2 5
【Sample Output】
(输出文件只包含一个整数,表示小 P 的物流公司完成阶段性工作所需要的最短时间。)
11
【样例解释】
将第1条航道改造成虫洞:则三个计划耗时分别为:11,12,11,故需要花费的时间为12。
将第2条航道改造成虫洞:则三个计划耗时分别为:7,15,11,故需要花费的时间为15。
将第3 条航道改造成虫洞:则三个计划耗时分别为:4,8,11,故需要花费的时间为11。
将第4条航道改造成虫洞:则三个计划耗时分别为:11,15,5,故需要花费的时间为15。
将第5条航道改造成虫洞:则三个计划耗时分别为:11,10,6,故需要花费的时间为11。
故将第3条或第5条航道改造成虫洞均可使得完成阶段性工作的耗时最短,需要花费的时间为11。
【数据范围】
【题解】二分答案+倍增+树上差分
题目给的任务是:给定一棵树和一些点对,现可以将一条树边的权值变为0,使点对最大距离最小并求最小距离。
{最大距离最小→二分答案}
这一点显然
{处理点对距离问题→倍增}
首先dfs求出树上每个点到根节点的距离d。
对于点对(xi,yi),dis[i]=d[xi]+d[yi]-2*d[lca(xi,yi)]。
{二分检验答案→树上差分}
设当前二分出来可能答案为s,什么样的边权值需变成0?
<1>首先从点对来看,对于dis[i]>x的点对,显然我们必须删掉其路径上的一条边;
<2>从边来看,如果删掉一条边能使所有的点对距离都小于等于x,那么这条边的权值 w>=max(dis[i])-x;
<3>问题转化为如何统计一条边在dis[i]>x的点对路径上的出现次数。这里用到树上查分来做。对于点对(xi,yi),设数组p,如果dis[i]>x,将p[xi]++, p[yi]++, p[lca(xi,yi)]–。这样做完有什么用呢?我们发现,如果此时统计以rt为根的子树的p数组和,得到的就是rt和father[rt]所连边要求的出现次数了。
//可以这么想,如果这条边被经过的话,必然一端在子树内,一端在子树外。这样统计起来就能得到答案。详见程序。
公元 2044 年,人类进入了宇宙纪元。
L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球。
小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之间不会产生任何干扰。
为了鼓励科技创新, L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。
在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后,这 m 个运输计划会同时开始,所有飞船一起出发。当这 m 个运输计划都完成时,小 P 的物流公司的阶段性工作就完成了。
如果小 P 可以自由选择将哪一条航道改造成虫洞, 试求出小 P 的物流公司完成阶段性工作所需要的最短时间是多少?
【Sample Input】
(第一行包括两个正整数 n,m,表示 L 国中星球的数量及小 P 公司预接的运输计划的数量,星球从 1 到 n 编号。
接下来 n−1 行描述航道的建设情况,其中第 i 行包含三个整数 ai,bi 和 ti,表示第 i 条双向航道修建在 ai 与 bi 两个星球之间,任意飞船驶过它所花费的时间为 ti。数据保证 1≤ai,bi≤n 且 0≤ti≤1000。
接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 uj 和 vj,表示第 j 个运输计划是从 uj 号星球飞往 vj号星球。数据保证 1≤ui,vi≤n)
6 3
1 2 3
1 6 4
3 1 7
4 3 6
3 5 5
3 6
2 5
【Sample Output】
(输出文件只包含一个整数,表示小 P 的物流公司完成阶段性工作所需要的最短时间。)
11
【样例解释】
将第1条航道改造成虫洞:则三个计划耗时分别为:11,12,11,故需要花费的时间为12。
将第2条航道改造成虫洞:则三个计划耗时分别为:7,15,11,故需要花费的时间为15。
将第3 条航道改造成虫洞:则三个计划耗时分别为:4,8,11,故需要花费的时间为11。
将第4条航道改造成虫洞:则三个计划耗时分别为:11,15,5,故需要花费的时间为15。
将第5条航道改造成虫洞:则三个计划耗时分别为:11,10,6,故需要花费的时间为11。
故将第3条或第5条航道改造成虫洞均可使得完成阶段性工作的耗时最短,需要花费的时间为11。
【数据范围】
【题解】二分答案+倍增+树上差分
题目给的任务是:给定一棵树和一些点对,现可以将一条树边的权值变为0,使点对最大距离最小并求最小距离。
{最大距离最小→二分答案}
这一点显然
{处理点对距离问题→倍增}
首先dfs求出树上每个点到根节点的距离d。
对于点对(xi,yi),dis[i]=d[xi]+d[yi]-2*d[lca(xi,yi)]。
{二分检验答案→树上差分}
设当前二分出来可能答案为s,什么样的边权值需变成0?
<1>首先从点对来看,对于dis[i]>x的点对,显然我们必须删掉其路径上的一条边;
<2>从边来看,如果删掉一条边能使所有的点对距离都小于等于x,那么这条边的权值 w>=max(dis[i])-x;
<3>问题转化为如何统计一条边在dis[i]>x的点对路径上的出现次数。这里用到树上查分来做。对于点对(xi,yi),设数组p,如果dis[i]>x,将p[xi]++, p[yi]++, p[lca(xi,yi)]–。这样做完有什么用呢?我们发现,如果此时统计以rt为根的子树的p数组和,得到的就是rt和father[rt]所连边要求的出现次数了。
//可以这么想,如果这条边被经过的话,必然一端在子树内,一端在子树外。这样统计起来就能得到答案。详见程序。
#include <cstdio> #include <iostream> #include <cstring> #define N 300005 struct edge{ int to,s,nxt;}e[N<<1]; struct plan{ int u,v,lca,dis;}a ; int n,m,l,r,mid,ans,cnt,mx,num,q ,fi ,f [20],d ,dis ,p ; bool bo ; inline int read() { int c=getchar(),t=0; for (;c<48 || 57<c;c=getchar()); do{ t=(t<<3)+(t<<1)+c-48; c=getchar(); }while(48<=c&&c<=57); return t; } void add(int u,int v,int w) { e[++cnt].to=v;e[cnt].s=w; e[cnt].nxt=fi[u];fi[u]=cnt; } void lca() { int h=1,t=1; memset(bo,false,sizeof(bo)); for (bo[q[1]=1]=true;h<=t;++h) for (int i=fi[q[h]];i;i=e[i].nxt) if (!bo[e[i].to]) { f[e[i].to][0]=q[h]; d[e[i].to]=d[q[h]]+1; bo[q[++t]=e[i].to]=true; for (int j=0,k=q[h];f[k][j];k=f[k][j++]) f[e[i].to][j+1]=f[k][j]; } } int findlca(int u,int v) { if (d[u]<d[v]) std::swap(u,v); for (int j;d[u]>d[v];u=f[u][j-1]) for (j=1;d[f[u][j]]>d[v];++j); for (int j;u!=v;u=f[u][j-1],v=f[v][j-1]) for (j=1;f[u][j]!=f[v][j];++j); return u; } int dfs(int x,int fa) { for (int i=fi[x];i;i=e[i].nxt) if (e[i].to!=fa) { dis[e[i].to]=dis[x]+e[i].s; dfs(e[i].to,x); } } bool dfs1(int x,int fa,int s) { for (int i=fi[x];i;i=e[i].nxt) if (e[i].to!=fa) { if (dfs1(e[i].to,x,e[i].s)) return true; p[x]+=p[e[i].to]; } if (s>=mx && p[x]>=num) return true; return false; } bool check(int x) { for (int i=1;i<=n;++i) p[i]=0; mx=num=0; for (int i=1;i<=m;++i) if (a[i].dis>x) { mx=std::max(mx,a[i].dis-x); ++p[a[i].u];++p[a[i].v]; p[a[i].lca]-=2;++num; } if (!num) return true; return dfs1(1,0,0); } int main() { n=read();m=read(); for (int i=1;i<n;++i) { int u,v,w; u=read();v=read();w=read(); add(u,v,w);add(v,u,w); } lca(); for (int i=1;i<=m;++i) { a[i].u=read();a[i].v=read(); a[i].lca=findlca(a[i].u,a[i].v); } dfs(1,0); for (int i=1;i<=m;++i) a[i].dis=dis[a[i].u]+dis[a[i].v]-(dis[a[i].lca]<<1); ans=-1; for (l=0,r=1e9;l<=r;) { mid=(l+r)>>1; if (check(mid)) r=mid-1,ans=mid; else l=mid+1; } printf("%d\n",ans); return 0; }
相关文章推荐
- BZOJ 4326: NOIP2015 运输计划 二分答案 树上差分
- NOIP2015 运输计划 二分答案+Tarjan LCA+树上差分
- LuoguP2680/UOJ150[NOIP2015] 运输计划 解题报告【二分答案+树上操作(LCA)+树上差分】
- [BZOJ4326][NOIP2015]运输计划(二分答案+树上差分)
- 【NOIP2015】运输计划(树上差分,二分答案)
- 【BZOJ4326】【二分答案】【树上差分】NOIP2015 D2T3 运输计划 题解
- [BZOJ4326][NOIP2015]运输计划(二分+dfs序+树上差分)
- bzoj4326【2015提高】运输计划(二分答案+lca+树上差分前缀和)
- BZOJ 4326 NOIP2015 运输计划 (二分+树上差分)
- NOIP 2015 Day2 T3 运输计划(二分+dfs序+树上差分+倍增LCA)
- 【BZOJ】4326 NOIP2015 运输计划 二分+LCA+树上差分
- [BZOJ4326][NOIP2015][倍增][二分][差分序列]运输计划
- NOIP 2015 Day 2 transport 运输计划 (树链剖分 序列差分 二分答案)
- bzoj 4326: NOIP2015 运输计划【树链剖分+二分+树上差分】
- Noip2015 运输计划 【二分答案】【差分】【LCA】
- [BZOJ4326][NOIP2015]运输计划(二分+dfs序+树上差分)
- LCA+二分+树上差分——Luogu2680 [NOIP2015]运输计划
- 【uoj#150】【NOIP2015】运输计划 树上前缀和+lca+二分+拓扑排序+特别的卡常数技巧
- NOIP2015 运输计划 (树链剖分,LCA,树上差分)
- [二分+差分]BZOJ 4326——NOIP2015 运输计划