您的位置:首页 > 其它

【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]所连边要求的出现次数了。

//可以这么想,如果这条边被经过的话,必然一端在子树内,一端在子树外。这样统计起来就能得到答案。详见程序。

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息