您的位置:首页 > 其它

bzoj4326 NOIP2015 运输计划

2016-04-13 11:55 246 查看
  二分答案,如果一条航道的长度大于当前二分的答案,那么很明显这条航道上需要有一条边权值变为0,且条边权值应该>=(航道长度-二分的答案),那么若想使得所以不满足条件的航道都满足条件,这个虫洞就应该设置在这些航道的交集上,且权值应>=(max(航道长度)-二分的答案),航道的交集具体实现可以把这条航道上路径次数都加1,假设不满足条件的航道有m条,那么一条边如果次数==m条,就表示其是m条航道的交集了,实现的话一个dfs就可以搞定,复杂度O(nlogn)

  代码

  

#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#define pb push_back
#define N 700100
using namespace std;
int dp,pre
,p
,tt
,ww
,fa
,deep
,v
,A
,B
,LCA
;
int s
[20],n,m,a,b,c,i,sum
,ans,cnt,dis
,dist
;
void link(int x,int y,int z)
{
dp++;pre[dp]=p[x];p[x]=dp;tt[dp]=y;ww[dp]=z;
}
void dfs(int x)
{
int i;
i=p[x];
while (i)
{
if (tt[i]!=fa[x])
{
deep[tt[i]]=deep[x]+1;
fa[tt[i]]=x;
v[tt[i]]=ww[i];
dis[tt[i]]=dis[x]+ww[i];
dfs(tt[i]);
}
i=pre[i];
}
}

int lca(int x,int y)
{
if(deep[x]>deep[y])x^=y^=x^=y;
int i;
for(i=19;i>=0;i--)
{
if(deep[y]-deep[x]>=(1<<i))
{
y=s[y][i];
}
}
if(x==y)return x;
for(i=19;i>=0;i--)
{
if(s[x][i]!=s[y][i])
{
x=s[x][i];
y=s[y][i];
}
}
return fa[x];
}
void gao(int x)
{
int i=p[x];
while (i)
{
if (tt[i]!=fa[x])
{
gao(tt[i]);
sum[x]+=sum[tt[i]];
}
i=pre[i];
}
}
int check(int x)
{
int cnt=0,dec=0;
for (i=1;i<=n;i++)
sum[i]=0;
for (i=1;i<=m;i++)
if (dist[i]>x)
{
cnt++;
dec=max(dec,dist[i]-x);
sum[A[i]]++;
sum[B[i]]++;
sum[LCA[i]]-=2;
}
gao(1);
for (i=1;i<=n;i++)
if ((sum[i]==cnt)&&(v[i]>=dec)) return 1;
return 0;
}
int main()
{
scanf("%d%d",&n,&m);
for (i=1;i<n;i++)
{
scanf("%d%d%d",&a,&b,&c);
link(a,b,c);
link(b,a,c);
}
dfs(1);
for(i=1;i<=n;i++)s[i][0]=fa[i];
for(int h=1;h<20;h++)
{
for(i=1;i<=n;i++)
{
s[i][h]=s[s[i][h-1]][h-1];
}
}
for (i=1;i<=m;i++)
{
scanf("%d%d",&A[i],&B[i]);
LCA[i]=lca(A[i],B[i]);
dist[i]=dis[A[i]]+dis[B[i]]-2*dis[LCA[i]];
}
int L=0,R=0;
for (i=1;i<=m;i++)
R=max(R,dist[i]);
int mid;
while (L<=R)
{
mid=(L+R)>>1;
if (check(mid)) R=mid-1;else L=mid+1;
}
printf("%d\n",L);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: