题解 P2680 【运输计划】

不得不说,这真是一道优(s)质(b)图()论()题.相信它能给拼搏于逐梦之路上的你有力的援助,让你感受到出题人的满满善意(大雾)

Solution [NOIP2015]运输计划

题目大意:给定一棵树,以及\(m\)条简单路径,你可以使任意一条边的权值变为\(0\),询问最大路径长度的最小值

二分答案,树上差分


分析:假设出题人良心发现不卡常,我们看看怎么做

"最大值最小"我们可以考虑二分

首先我们把路径长度求出来(这玩意儿可以树上差分 / 倍增),然后对其按升序排序

差分做法:设有两点\(u,v\),\(LCA\)为\(l\),\(dis[u]\)为\(u\)到根节点路径长度,则路径\((u,v)\)长为\(dis[u] + dis[v] - 2 \times dis[l]\)

假设我们二分一个答案\(limit\),我们可以很快的把所有长度\(dis > limit\)的路径找出来,假设这样的路径有\(num\)个

然后我们找被\(num\)个路径共同经过的边,如果没有,那么可以判\(limit\)这个答案为\(false\)(因为无论你怎么改总有路径长度\(>limit\))

然后假如有多条这样边,贪心选取其中权值最大的,看一下长度最大的路径减去这个边权后长度是否\(\leq limit\)即可

于是问题变成了把一段路径上所有边权增加\(1\),询问一条边的边权,我们可以考虑把边权下放到深度较深的点,然后继续树上差分

假如要修改\(u,v\)的话,我们把\(sum[u]+1,sum[v]+1,sum[l]-2\),然后子树和就是点权(即为边权)

下面就是喜闻乐见的卡常时间

  • \(1.\)发现求\(LCA\)次数较多,而且可以离线做,考虑用\(Tarjan\)算法优化
  • \(2.\)读入优化,配合\(fread\)食用更佳
  • \(3.\)子树求和,求出树的\(dfs\)序,然后倒着用\(dp\)刷表法思想刷新父亲,因为儿子一定比父亲晚出现,这个优化让我最大数据点从\(1400ms+\)到\(327ms\)……
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 3e5 + 100;
namespace FastIO{
    typedef int type;
    const int bufsize = 1 << 20;
    char buf[bufsize],*s,*t;
    inline char gc(){
        if(s == t)s = buf,t = buf + fread(buf,sizeof(char),bufsize,stdin);
        return *(s++);
    }
    inline type read(){
        type ret = 0;char c = gc();
        while(c < '0' || c > '9')c = gc();
        while(c >= '0' && c <= '9'){
            ret = ret * 10 + c - '0';
            c = gc();
        }
        return ret;
    }
}using FastIO::read;
struct Ques{int v,id;}ques[maxn << 1];
struct Edge{int to,dist;}Edges[maxn << 1];
int head[maxn],nxt[maxn << 1],headq[maxn],nxtq[maxn << 1];
inline void addques(int u,int v,int id){
    static int tot;
    ques[++tot] = Ques{v,id};
    nxtq[tot] = headq[u];
    headq[u] = tot;
}
inline void addedge(int from,int to,int dist){
    static int tot;
    Edges[++tot] = Edge{to,dist};
    nxt[tot] = head[from];
    head[from] = tot;
}

struct Road{int u,v,lca,val;}road[maxn];
int f[maxn],dfn[maxn],rnk[maxn],faz[maxn],sum[maxn],val[maxn],n,m,dis[maxn],l,r,ans,dfs_tot;
inline int find(int x){return x == f[x] ? x : f[x] = find(f[x]);}
inline void dfs_init(int u = 1){
    f[u] = u;
    dfn[u] = ++dfs_tot;
    rnk[dfn[u]] = u;
    for(int i = head[u];i;i = nxt[i]){
        const Edge &e = Edges[i];
        if(dfn[e.to])continue;
        dis[e.to] = dis[u] + e.dist;
        val[e.to] = e.dist;
        faz[e.to] = u;
        dfs_init(e.to);
        f[e.to] = u;
    }
    for(int i = headq[u];i;i = nxtq[i]){
        const Ques &q = ques[i];
        if(dfn[q.v])road[q.id].lca = find(q.v);
    }
}
inline int query(int u,int v,int l){return dis[u] + dis[v] - 2 * dis[l];}
inline void modify(int u,int v,int l){sum[u]++,sum[v]++,sum[l] -= 2;}
inline int max(int a,int b){return a > b ? a : b;}
inline bool check(int limit){
    if(road[m].val <= limit)return true;
    memset(sum,0,sizeof(sum));
    int now = 0,mx = -1;
    for(int i = m;i >= 1 && road[i].val > limit;i--,now++)
        modify(road[i].u,road[i].v,road[i].lca);
    for(int i = n;i >= 1;i--)
        sum[faz[rnk[i]]] += sum[rnk[i]];
    for(int i = 1;i <= n && road[m].val - mx > limit;i++)
        if(sum[i] == now)mx = max(mx,val[i]);
    return road[m].val - mx <= limit;
}
int main(){
    n = read(),m = read();
    for(int u,v,w,i = 1;i < n;i++)
        u = read(),v = read(),w = read(),addedge(u,v,w),addedge(v,u,w);
    for(int i = 1;i <= m;i++)
        road[i].u = read(),road[i].v = read(),addques(road[i].u,road[i].v,i),addques(road[i].v,road[i].u,i);
    dfs_init();
    for(int i = 1;i <= m;i++)
        road[i].val = query(road[i].u,road[i].v,road[i].lca),r = max(r,road[i].val);
    sort(road + 1,road + 1 + m,[](const Road &a,const Road &b){return a.val < b.val;});
    int tot = 0;
    while(l <= r){
        int mid = (l + r) >> 1;
        tot++;
        if(check(mid))ans = mid,r = mid - 1;
        else l = mid + 1;
    }
    printf("%d\n",ans);
    return 0;
}
上一篇:python基础--切片


下一篇:PTA A1003&A1004