【NOIP2015_D2_T3】运输计划 / 树上差分、二分

大意:
给定一棵 n n 个节点的带边权的树,m 条树上路径。你能将一条边的边权变成 0 0 ,问这 m 条路径中最长路径的长度最小值是多少。

最大值最小,可以想到二分。我们二分 最大值能否 ans ≤ a n s
对于每条长度(假设为 len l e n )超过 ans a n s 的边,我们就肯定至少要去掉 lenans l e n − a n s 的长度。我们可以记 c[i] c [ i ] 为每条边在这种长度超过 ans a n s 的边里出现了几次,然后如果有“都出现,并且去掉能符合要求”的边,就能;反之则不能。
怎样判断“去掉能符合要求”?因为前面说了对于每条超出的边,至少去掉 lenans l e n − a n s ,所以我们可以记个最大值然后和这条边的边权比一下就行了。
怎样高效地记录 c[i] c [ i ] 呢?可以用数据结构,但这是静态的直接差分就可以了,树上差分了解一下。
代码:(洛谷上能过,YZOJ被卡常了5分。。)

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

#define R register
#define Maxn 300001
#define LG 20
#define getchar() getchar_unlocked()  //本机编译请去掉这个

int n,m;
int first[Maxn],next[Maxn<<1],to[Maxn<<1],cnt;
int fa[Maxn],t[Maxn<<1],dis[Maxn];

template <class RME> inline void read(R RME &x)
{
    x=0;R bool f=0;R char ch;
    for(ch=getchar();ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
    if(f) f=-x; 
}
struct query
{
    int u,v,lc,diss;
}q[Maxn];
int dep[Maxn],f[Maxn][22],c[Maxn];
inline void link(R int x,R int y,R int z)
{
    next[++cnt]=first[x];
    first[x]=cnt;
    to[cnt]=y;
    t[cnt]=z;
}
void dfs(R int now)
{
    for (R int i=first[now];i;i=next[i])
    {
        if(to[i] != f[now][0])
        {
            f[to[i]][0] = now;
            dep[to[i]] = dep[now] + 1;
            dis[to[i]] = dis[now] + t[i];
            fa[to[i]] = i; 
            dfs(to[i]); 
        }
    }
}
void dfs1(R int now)
{
    for (R int i=first[now];i;i=next[i])
    {
        if(to[i] != f[now][0])
        {
            dfs1(to[i]);
            c[now] += c[to[i]];
        }
    }
}
inline int lca(R int x,R int y)
{
    if(dep[x] > dep[y]) swap(x,y);
    R int tmp=dep[y]-dep[x];
    for (R int i=0;i<LG && (1<<i) <= tmp;++i)(tmp&(1<<i)) ? y=f[y][i] : 0;
    for (R int i=LG-1;i>=0;--i)
    (f[x][i]^f[y][i]) ? x=f[x][i],y=f[y][i] : 0;
    return (x^y) ? f[x][0] : x;
}
inline bool check(R int ans)
{
    R int tot=0,ned=0;
    memset(c,0,sizeof c);
    for (R int i=1;i<=m;++i)
    {
        if(q[i].diss > ans)
        {
            ++tot;
            c[q[i].u] ++;
            c[q[i].v] ++;
            c[q[i].lc] -=2;
            ned=max(ned,q[i].diss-ans);
        }
    }
    dfs1(1);
    for (R int i=2;i<=n;++i)
     if(c[i] == tot && t[fa[i]] >= ned) return 1;
    return 0;   
}
int main()
{
    R int l=-1,r=0;
    read(n);read(m);
    for (R int i=1,a,b,w;i<n;++i)
    { 
        read(a),read(b),read(w);
        link(a,b,w); link(b,a,w);
    }
    dfs(1);
    for (R int i=1;i<=LG;++i)
     for (R int j=1;j<=n;++j)
      f[j][i]=f[f[j][i-1]][i-1];
    for (R int i=1;i<=m;++i)
    {
        read(q[i].u),read(q[i].v);
        q[i].lc=lca(q[i].u,q[i].v);
        q[i].diss = dis[q[i].u]+dis[q[i].v]-(dis[q[i].lc]<<1);
        r=max(q[i].diss,r);
    }
    l=r-1001;
    while(r-l!=1)
    {
        R int mid=(l+r)>>1;
        if(check(mid)) r=mid;
        else l=mid;
    }
    printf("%d",r);
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值