[二分+LCA+差分]【NOIP2015D2T3】运输计划 题解

来源:互联网 发布:淘宝代购lv是真的吗 编辑:程序博客网 时间:2024/05/29 15:20

传送门:
洛谷:https://daniu.luogu.org/problemnew/show/2680#sub
(链接给的是大牛分站的,主站上T了一个点,大牛分站A了,“高性能”,卡常数)
UOJ:http://uoj.ac/problem/150

题目分析

给出一棵n个节点且有边权的数和m个从siti的运输计划,完成所有计划所花费的时间为运输计划中的最大时间。现在可以选中树上的一条边并把它的边权变为0。求边权变化后完成所有计划所需时间的最小值。

解题分析

最大中求最小,套路想到二分。二分答案ans,预先LCA求出每个任务所需时间,判断出有多少个运输计划超出了ans。

因为题目中要求只能选一条边,所以我们肯定要求这条边在所有超出ans的运输计划,而且这条边肯定尽可能地大。可以用差分来找出有没有这条边。先刷出所有超出ans的运输计划个数k和最大的差距tem,然后sum数组,对于每一个计划sum[si]加1,sum[ti]加1,sum[LCA(si,ti)]减2。如果一条边被所有超出ans的运输计划覆盖,那么有sum[son[j]]=k,判断这条边能否减少到ans一下,则w[j]>=tem,不过代码中为了好写把这些赋值在点上。

注意每个任务的LCA值建议存储起来节省空间。

复杂度:
时间:O(mlogn+n log ai)
空间:O(n)

#include<cstdio>#include<cstring>#include<algorithm>#define maxn 300005#define maxe 600005using namespace std;int n,m,tot,len,ans,s[maxn],t[maxn],dis[maxn],cst[maxn],son[maxe],nxt[maxe],w[maxe],lnk[maxn],dep[maxn],fa[maxn][20],sum[maxn],v[maxn],la[maxn];inline char nc(){    static char buf[100000],*p1=buf,*p2=buf;    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;}inline void readi(int &x){    x=0; char ch=nc();    while ('0'>ch||ch>'9') ch=nc();    while ('0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=nc();}}void _add(int x,int y,int z){    son[++tot]=y; w[tot]=z; nxt[tot]=lnk[x]; lnk[x]=tot;}void _dfs(int x,int dad){    for (int j=lnk[x];j;j=nxt[j])        if (son[j]!=dad){            dep[son[j]]=dep[x]+1; dis[son[j]]=dis[x]+w[j]; _dfs(son[j],x); fa[son[j]][0]=x; v[son[j]]=w[j];        }}void _init(){    freopen("transport.in","r",stdin);    freopen("transport.out","w",stdout);    readi(n); readi(m); dis[1]=dep[0]=0;    for (int i=1,x,y,z;i<n;i++){        readi(x); readi(y); readi(z);        _add(x,y,z); _add(y,x,z);    }    dep[1]=1;_dfs(1,0); len=19;    for (int j=1;j<=len;j++)        for (int i=1;i<=n;i++)            fa[i][j]=fa[fa[i][j-1]][j-1];}int _LCA(int x,int y){    if (dep[x]<dep[y]) swap(x,y);    for (int j=len;j>=0&&dep[x]>dep[y];j--) if (dep[fa[x][j]]>=dep[y]) x=fa[x][j];    if (x==y) return x;    for (int j=len;j>=0;j--)        if (fa[x][j]!=fa[y][j]) {x=fa[x][j]; y=fa[y][j];}    return fa[x][0];}void dfsc(int x,int dad){    for (int j=lnk[x];j;j=nxt[j])        if (son[j]!=dad) {dfsc(son[j],x); sum[x]+=sum[son[j]];}}bool _check(int x){    int tem=0,k=0; memset(sum,0,sizeof(sum));    for (int i=1;i<=m;i++)        if (cst[i]>x){            sum[s[i]]++; sum[t[i]]++; sum[la[i]]-=2;            k++; tem=max(tem,cst[i]-x);        }    if (tem==0) return 1;    dfsc(1,0); for (int i=2;i<=n;i++) if (sum[i]==k&&v[i]>=tem) return 1;    return 0;}void _solve(){    int L=0,R=0,mid;    for (int i=1;i<=m;i++){        readi(s[i]); readi(t[i]); la[i]=_LCA(s[i],t[i]);        cst[i]=dis[s[i]]+dis[t[i]]-(dis[la[i]]<<1); R=max(R,cst[i]);    }    while (L<=R){        mid=(R-L)/2+L;        if (_check(mid)) R=mid-1; else L=mid+1;    }    printf("%d",L);}int main(){    _init();    _solve();    return 0;}
阅读全文
0 0
原创粉丝点击