bzoj4326: NOIP2015 运输计划(lca+二分)

17 篇文章 0 订阅
9 篇文章 0 订阅

题目传送门
好题啊。

解法:
比较综合。。
我一开始想:
先把所有距离求出来。
然后二分答案。
所有的路径距离>答案 说明需要建虫洞。
在所有大于答案的路径中是有交集的。
那么我们只需要在交集中找到一条边使得所有路径-边权<=答案。
然后用差分咯。
sum[i]表示1到i这条边次数都加1。
然后就差分嘛。
对于每个x和y。
1到x路径+1
1到y路径+1
1到lca路径-2就行了嘛。

代码实现:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
struct node {int x,y,c,next;}a[2100000];int len,last[1100000];
void ins(int x,int y,int c) {len++;a[len].x=x;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;}
int n,m,mn[21][610000],dfn,dep[1100000],ys[1100000],bin[21],dist[1100000],Lca[1100000],to[1100000];//ys表示与父亲的边权 
void dfs(int x) {
    for(int k=last[x];k;k=a[k].next) {
        int y=a[k].y;
        if(y!=mn[0][x]) {mn[0][y]=x;dep[y]=dep[x]+1;ys[y]=a[k].c;to[y]=to[x]+a[k].c;dfs(y);}
    }
}
void work() {
    bin[0]=1;for(int i=1;i<=20;i++)bin[i]=bin[i-1]*2;
    for(int j=1;j<=20;j++)for(int i=1;i<=n;i++)if(dep[i]>=bin[j])mn[j][i]=mn[j-1][mn[j-1][i]];
}
int lca(int x,int y) {
    if(dep[x]>dep[y])swap(x,y);
    for(int i=20;i>=0;i--)if(dep[y]-dep[x]>=bin[i])y=mn[i][y];
    if(x==y)return x;
    for(int i=20;i>=0;i--)if(mn[i][y]!=mn[i][x]&&dep[x]>=bin[i]){x=mn[i][x];y=mn[i][y];}
    return mn[0][x];
}
int xx[1100000],yy[1100000],sum[1100000];
void cf(int x) {
    for(int k=last[x];k;k=a[k].next) {
        int y=a[k].y;
        if(y!=mn[0][x]) {cf(y);sum[x]+=sum[y];}
    }
}
int main() {
    scanf("%d%d",&n,&m);len=0;memset(last,0,sizeof(last));
    for(int i=1;i<n;i++) {int x,y,c;scanf("%d%d%d",&x,&y,&c);ins(x,y,c);ins(y,x,c);}
    mn[0][1]=0;dep[1]=0;ys[1]=0;to[1]=0;dfs(1);work();
    int l=1,r=0,mid,ans=0;
    for(int i=1;i<=m;i++) {
        scanf("%d%d",&xx[i],&yy[i]);Lca[i]=lca(xx[i],yy[i]);
        dist[i]=to[xx[i]]+to[yy[i]]-2*to[Lca[i]];r=max(r,dist[i]);
    }
    while(l<=r) {
        mid=(l+r)/2;int mmax=0,tot=0;memset(sum,0,sizeof(sum));
        for(int i=1;i<=m;i++)if(dist[i]>mid) {
            tot++;mmax=max(mmax,dist[i]-mid);sum[xx[i]]++;sum[yy[i]]++;sum[Lca[i]]-=2;
        }cf(1);
        bool bk=false;
        for(int i=1;i<=n;i++)if(sum[i]>=tot&&ys[i]>=mmax) {bk=true;break;}
        if(bk==true) {r=mid-1;ans=mid;}
        else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值