1999: [Noip2007]Core树网的核 树形dp 单调队列

据说当年的数据范围是500….,数据好像随便YY怎么都能过的样子 = =
算了,我们来考虑正解
首先,答案不管在哪一条直径上讨论都是唯一的……我不会证明0.0
其次,假如说我们在直径上进行决策,就是在直径上选出了一段区间 <=s <script type="math/tex" id="MathJax-Element-1"><=s</script> ,这段区间要怎么决策呢?求每一个点到他的距离?显然不是,我们可以把直径抻直,被直径上的点挂着的树构成一个外向树森林,然后求出每一个小树到直径上对应根的距离 disMAX
在决策的时候,可以发现,决策点只有区间两边的端点到直径两端点的距离,以及区间上最大的 disMAX 。为什没有区间外的 disMAX ,显然,如果他的 disMAX 比直径的端点到区间左端点还大,那么现在的直径就不是直径了
然后我们只需要维护一个长度 <=s <script type="math/tex" id="MathJax-Element-6"><=s</script>的一段区间,维护一个单调队列,每次决策就行了
时间复杂度 O(n)

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <queue> 
using namespace std;
#define N 500000+5
#define M 1000000+5

const int inf = 2147483647;

int head[N];

struct graph
{
    int next,to;
    int val;
    graph () {}
    graph (int _next,int _to,int _val)
    :next(_next),to(_to),val(_val){}
}edge[M];

inline void add(int x,int y,int z)
{
    static int cnt = 0 ;
    edge[++cnt] = graph(head[x],y,z);
    head[x] = cnt;
    edge[++cnt] = graph(head[y],x,z);
    head[y] = cnt;
}

inline int read()
{
    int x=0,f=1;char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-')f=-1;ch = getchar();}
    while(ch >='0' && ch <='9'){x=(x<<1)+(x<<3)+ch-'0';ch = getchar();}
    return x*f;
}

int LMAX[N],RMAX[N],MAX[N],dis[N];
int path[N];
bool in[N];
int len;

struct Lux
{
    int x,from;
    int dis;
    Lux () {}
    Lux (int _x,int _from,int _dis = 0)
    :x(_x),from(_from),dis(_dis){}
};

queue <Lux> q;

void Getdis(int x)
{
    q.push(Lux(x,x));
    while(!q.empty())
    {
        Lux tmp = q.front();
        q.pop();
        int tt = tmp.x;
        for(int i=head[tt];i;i=edge[i].next)
            if(edge[i].to!=tmp.from)
            {
                dis[edge[i].to] = dis[tt] + edge[i].val;
                q.push(Lux(edge[i].to,tt));
            }
    }
}

int GetMAX(int x,int fa)
{
    int MAX = 0;
    /*for(int i=head[x];i;i=edge[i].next)
        if(!in[edge[i].to]&&edge[i].to!=fa)
            MAX = max(MAX,GetMAX(edge[i].to,x)+edge[i].val);
    return MAX;*/
    q.push(Lux(x,fa,0));
    while(!q.empty())
    {
        Lux tmp = q.front();
        int dis = tmp.dis;
        q.pop();
        MAX = max(MAX,dis);
        int tt = tmp.x;
        for(int i=head[tt];i;i=edge[i].next)
            if(edge[i].to!=tmp.from && !in[edge[i].to])
                q.push(Lux(edge[i].to,tt,dis+edge[i].val));
    }
    return MAX;
}

int from[N];

bool find(int x,int tmp,int fa)
{
    /*if(x==tmp)
    {
        path[++len] = tmp;
        return 1;
    }
    for(int i=head[x];i;i=edge[i].next)
        if(edge[i].to!=fa && find(edge[i].to,tmp,x))
            {path[++len] = x;return 1;}
    return false;*/
    q.push(Lux(x,x));   
    while(!q.empty())
    {
        Lux tmp1 = q.front();
        q.pop();
        int tt = tmp1.x;
        for(int i=head[tt];i;i=edge[i].next)
            if(edge[i].to!=tmp1.from)
            {
                from[edge[i].to] = tt;
                q.push(Lux(edge[i].to,tt));
            }
    }
    int tt = tmp;
    while(tt!=x)
    {
        path[++len] = tt;
        tt = from[tt];
    }
    path[++len] = x;
    return 1;
}

int que[N];

int main()
{
//  freopen("01.in","r",stdin);
    int n = read() ;
    int s = read();
    for(int i=1;i<n;++i)
    {
        int a = read() , b = read() ;
        int c = read();
        add(a,b,c);
    }
    int A = 1, B = 1;
    Getdis(1);
    for(int i=1;i<=n;++i)
        if(dis[i] >= dis[A])
            A = i;
    dis[A] = 0;
    Getdis(A);
    for(int i=1;i<=n;++i)
        if(dis[i]>=dis[B])
            B = i;
    find(B,A,0);
    for(int i=1;i<=len;++i)
        in[path[i]] = 1;
    for(int i=1;i<=len;++i)
        MAX[i] = GetMAX(path[i],0);
    for(int i=1;i<=len;++i)
        LMAX[i] = dis[path[i]] - dis[path[1]],RMAX[i] = dis[path[len]] - dis[path[i]];
//  cout<<len<<endl;
//  for(int i=1;i<=len;++i)
//      cout<<path[i]<<" ";
//  return 0 ;
    int l = 0, r = 0;
    int ans = inf, tmp;
    for(int i = 1, j = 1; i <= len; i++)
    {
        if(l != r && que[l] < i) que[l++] = 0;
        while(j <= n && dis[path[j]] - dis[path[i]] <= s)
        {
            while(l != r && MAX[que[r-1]] <= MAX[j]) que[--r] = 0;
            que[r++] = j++;
        }
        tmp = max(MAX[que[l]], max(LMAX[i], RMAX[j-1]));
        ans = min(ans,tmp);
    }
    cout<<ans;
}       
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值