BZOJ 4326: NOIP2015 运输计划

3 篇文章 0 订阅
2 篇文章 0 订阅

去年这道题没做出来
然而今儿看到正解也是崩溃的
正解是个垃圾树剖。。
noip 什么时候开始考树剖了???
好吧去年联赛的时候还没学树剖。。。


第一遍 dfs 除了处理那些个节点的重量外顺便处理下每个节点到根的距离
其他和普通树剖没什么区别
然后读入点对,先算出每一对的 lca 和距离,记下最大距离
二分答案,因为最长的那条路线是肯定要变短的,所以二分的上界就是最长的那条
对于一个答案,一个点对的距离如果大于这个数就在线段树上覆盖一次,最后每一条边统计覆盖次数,并找出覆盖次数等于距离大于答案的点对个数的最长的一条边,然后判断最长那条是否小于等于答案加该边长
覆盖什么的和树剖路径上修改的一样


本以为要调很久没想到一下就过了
本来 1A 的然而我文件没关 T 了一次。。。mdzz
去掉文件就直接过了。。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define g getchar()
#define ll long long
#define inf 0x3f3f3f3f
#define maxn 300010
using namespace std;
inline ll read(){
    ll x=0,f=1;char ch=g;
    for(;ch<'0'||ch>'9';ch=g)if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=g)x=x*10+ch-'0';
    return x*f;
}
inline void out(ll x){
    int a[25],t=0;
    if(x<0)putchar('-'),x=-x;
    for(;x;x/=10)a[++t]=x%10;
    for(int i=t;i;--i)putchar('0'+a[i]);
    if(t==0)putchar('0');
    putchar('\n');
}
struct re{int v,w,next;}ed[maxn*2];
int e,cnt,m,n,mx,lca[maxn],x[maxn],y[maxn],ans;
int sz[maxn],head[maxn],f[maxn],dis[maxn],bl[maxn],id[maxn],num[maxn],t[3*maxn],dist[maxn];
inline void ins(int x,int y,int w){ed[++e]=(re){y,w,head[x]};head[x]=e;}
void dfs1(int x){
    sz[x]=1;
    for(int i=head[x];i;i=ed[i].next)
    if(ed[i].v!=f[x]){
        f[ed[i].v]=x;dis[ed[i].v]=dis[x]+ed[i].w;
        dfs1(ed[i].v);sz[x]+=sz[ed[i].v];
    }
}
void dfs2(int x,int chain){
    int now=0;bl[x]=chain;id[x]=++cnt;num[cnt]=x;
    for(int i=head[x];i;i=ed[i].next)if(ed[i].v!=f[x]&&sz[now]<sz[ed[i].v])now=ed[i].v;
    if(now)dfs2(now,chain);
    for(int i=head[x];i;i=ed[i].next)if(ed[i].v!=now&&ed[i].v!=f[x])dfs2(ed[i].v,ed[i].v);
}
int lc(int x,int y){
    int fx=bl[x],fy=bl[y];
    for(;fx!=fy;){
        if(dis[fx]<dis[fy])swap(x,y),swap(fx,fy);
        x=f[fx];fx=bl[x];
    }
    return dis[x]<dis[y]?x:y;
}
inline void push(int k,int l,int r){if(l>=r)return;t[k<<1]+=t[k],t[k<<1|1]+=t[k];t[k]=0;}
int query(int k,int l,int r,int cnt){
    push(k,l,r);
    if(l>r)return 0;
    if(l==r)return t[k]==cnt?dis[num[l]]-dis[f[num[l]]]:0;
    int mid=(l+r)>>1;
    return max(query(k<<1,l,mid,cnt),query(k<<1|1,mid+1,r,cnt));
}
void add(int k,int l,int r,int x,int y){
    if(x>y)return;
    if(l==x&&r==y){++t[k];return;}
    push(k,l,r);
    int mid=(l+r)>>1;
    if(mid>=x)add(k<<1,l,mid,x,min(mid,y));
    if(mid<y)add(k<<1|1,mid+1,r,max(x,mid+1),y);
}
void clear(int k,int l,int r){
    t[k]=0;if(l==r)return;int mid=(l+r)>>1;clear(k<<1,l,mid);clear(k<<1|1,mid+1,r);
}
void add(int x,int y){
    for(;bl[x]!=bl[y];){
        add(1,1,n,id[bl[y]],id[y]);
        y=f[bl[y]];
    }
    add(1,1,n,id[x]+1,id[y]);
}
bool check(int t){
    int mx=0,cnt=0;clear(1,1,n);
    for(int i=1;i<=m;++i){
        if(t<dist[i])
        add(lca[i],x[i]),
        add(lca[i],y[i]),
        mx=max(mx,dist[i]),++cnt;
    }
    int ans=-query(1,1,n,cnt);
    return mx+ans<=t;
}
int main(){
    n=read();m=read();
    for(int i=1;i<n;++i){
        int x=read(),y=read(),w=read();
        ins(x,y,w);ins(y,x,w);
    }
    dfs1(1);dfs2(1,1);
    for(int i=1;i<=m;++i){
        x[i]=read(),y[i]=read(),lca[i]=lc(x[i],y[i]);
        dist[i]=dis[x[i]]+dis[y[i]]-2*dis[lca[i]];
        mx=max(dist[i],mx);
    }
    int l=0,r=mx;
    for(;l<=r;){
        int mid=(l+r)>>1;
        if(mid==11){
            mid=0;
            mid=11;
        }
        if(check(mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    out(ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值