【NOIP2015】【bzoj4326】运输计划 LCA+差分+二分答案

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

这里写图片描述
这里写图片描述
NOIP2015最后一题,实际上不难,艹起来比D1T3(斗地主)舒服多了


听说要卡常,求LCA就从倍增换成了tarjan,但不知道noip的老爷机过得了不,反正bzoj我感觉7860ms还不是很慢


然后大概思路很简单,先二分答案,检验的时候差分,在对所有比枚举值大的路径lca-=2,s++,v++然后dfs找都经过的边就可以了
程序见下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<algorithm>
#include<vector>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<stack>
#define INF 2100000000
#define ll long long
#define clr(x)  memset(x,0,sizeof(x))
#define maxclr(x)  memset(x,127,sizeof(x))
#pragma comment(linker,"/STACK:102400000,1024000")

using namespace std;

inline int read()
{
    char c='q';
    int ret=0;
    while(!(c>='0'&&c<='9'))
        c=getchar();
    while(c>='0'&&c<='9')
    {
        ret=(c-'0')+(ret<<1)+(ret<<3);
        c=getchar();
    }
    return ret;
}
#define M 300005

int first[M],lext[M<<1],to[M<<1],d[M<<1],t;
int fa[M],ufa[M],lca[M],lcad[M],vis[M],dis[M];
int first2[M],next2[M<<1],to2[M<<1],num[M<<1],t2;
int S[M],V[M],cf[M];
int n,m,mx,sum;

void addedge(int s,int v,int val)
{
    lext[++t]=first[s];
    first[s]=t;
    to[t]=v;
    d[t]=val;
}

void addedge2(int s,int v,int fig)
{
    next2[++t2]=first2[s];
    first2[s]=t2;
    to2[t2]=v;
    num[t2]=fig;
}

int get_fa(int x)
{
    return x==ufa[x]?x:ufa[x]=get_fa(ufa[x]);
}

inline void tarjan(int x,int w)
{
    dis[x]=w;
    vis[x]=1;
    ufa[x]=x;
    for(int i=first[x];i;i=lext[i])
    {
        int v=to[i];
        if(v==fa[x])continue;
        fa[v]=x;
        tarjan(v,w+d[i]);
        ufa[v]=x;
    }
    for(int i=first2[x];i;i=next2[i])
    {
        int v=to2[i],p=num[i];
        if(!vis[v])continue;
        lca[p]=get_fa(v);
        lcad[p]=dis[x]+dis[v]-dis[lca[p]]*2;
        mx=max(mx,lcad[p]);
    }
}

inline void dfs(int x)
{
    for(int i=first[x];i;i=lext[i])
    {
        int v=to[i];
        if(v==fa[x])continue;
        dfs(v);
        if(cf[v]==sum)
            mx=max(mx,d[i]);
        cf[x]+=cf[v];

    }
}

bool check(int x)
{
    sum=0;
    int maxx=0;
    clr(cf);
    for(int i=1;i<=m;i++)
        if(lcad[i]>x)
        {
            sum++;
            cf[S[i]]++;
            cf[V[i]]++;
            cf[lca[i]]-=2;
            maxx=max(maxx,lcad[i]);
        }
    if(sum==0)return 1;
    mx=0;
    dfs(1);
    return maxx-mx<=x;
}

int main()
{
    freopen("transport.in","r",stdin);
    freopen("transport.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<n;i++)
    {
        int a=read(),b=read(),c=read();
        addedge(a,b,c);
        addedge(b,a,c);
    }
    for(int i=1;i<=m;i++)
    {
        int a=read(),b=read();
        addedge2(a,b,i);
        addedge2(b,a,i);
        S[i]=a;
        V[i]=b;
    }
    tarjan(1,0);
    int l=0,r=mx+1,mid;
    while(r>l)
    {
        mid=(l+r)>>1;
        if(check(mid))r=mid;
        else l=mid+1;
    }
    printf("%d",l);
    return 0;
}

大概就是这个样子,如果有什么问题,或错误,请在评论区提出,谢谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值