天天看點

bob的race

HUD4123 Bob’s Race

題目描述:

首先給出一棵樹,n~5e4,然後要求求出每個點出發走不重複路徑到達的最遠的距離.之後,找出最長的連續的節點的長度,使得他們的之中最大值減去最小值小于等于給出的Q.一共問了M~500次Q.

題解:

完全分為兩部分:先求出每個點最遠到達長度.常用的樹形dp.首先一次dp出節點u為根的dpm和dps,保證這兩個沒有相交的路徑.之後再dfs出u,含義是u往上身的最長長度,即是說把u的兒子們都砍了,以u出發的最長長度.這個是dp的關鍵,就是u,fa,fa_len,那麼u的長度可能是先到fa,再用dp[fa],也可能先到fa再用fa的子樹,并且這個子樹不能是u删掉的那些. 兩次dfs完了之後,取大就好.

第二部分,分别搞到每個a[i],求相差小于Q的最大長度. 首先這樣一定是尺寸法,下面有一個标準的尺寸法的寫法.然後怎麼判定?可以用兩個單調隊列來維護,這個方法很好很直覺.也可以用RMQ直接查詢最大值和最小值.下面用RMQ來實作,順便固定下RMQ的姿勢

重點:

(1)樹形dp求樹上每一個點跑到最遠的長度.(2)給出數組用尺寸法求出連續的相差小于等于Q的最大長度.

代碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(int i = a;i < b;i++)
#define REP_D(i, a, b) for(int i = a;i <= b;i++)

typedef long long ll;

using namespace std;

const int maxn =  + ;
const int INF = INT_MAX/ -;
int ans[maxn], dp_m[maxn], dp_s[maxn];
int n, m;
struct info
{
    int to, len;
};

vector<info> G[maxn];

void dfs(int u, int fa)//第一次dfs
{
    int flag = ;//最好厘清楚葉子節點,防止有特殊情況發生.
    dp_m[u] = -INF;//初始化,嚴格區分m和s
    dp_s[u] = -INF;
    REP(i, , G[u].size())
    {
        int v = G[u][i].to, len = G[u][i].len;
        if(v!=fa)
        {
            flag = ;
            dfs(v, u);
            int temp = dp_m[v] + len;//隻用子樹的m來更新,因為如果隻有一個兒子,那麼因為路徑不能夠重複,是以u的s應該是-INF的.
            if(temp > dp_m[u])
            {
                dp_s[u] = dp_m[u];
                dp_m[u] = temp;
            }
            else if(temp > dp_s[u])
            {
                dp_s[u] = temp;
            }
        }
    }
    if(flag==)//葉子節點
    {
        dp_m[u] = ;
    }
}

void getAns_dfs(int u, int fa, int pre_len)//pre_len指的是fa到u的長度,友善下面使用.
{
    ans[u] = ;//ans就是砍掉u的兒子u往上面發出的最大長度
    int &f = ans[u];
    if(fa == )//最根的根節點是0
    {
        ;
    }
    else
    {
        if(dp_m[fa]!=-INF&&dp_m[fa]==dp_m[u]+pre_len)//如果fa的m正好是坎了
        {
            if(dp_s[fa]!=-INF)
            {
                int temp = dp_s[fa] + pre_len;
                f = max(f, temp);
            }
        }
        if(dp_m[fa]!=-INF&&dp_m[fa]!=dp_m[u]+pre_len)//沒有砍
        {
            int temp = dp_m[fa] + pre_len;
            f = max(f, temp);
        }
        f = max(f, ans[fa] + pre_len);//直接上去fa,沒有用到fa的子樹
    }
    REP(i, , G[u].size())
    {
        int len = G[u][i].len, v = G[u][i].to;
        if(v!=fa)
        {
            getAns_dfs(v, u, len);//繼續傳進去dfs
        }
    }
}
const int T_MAX = * + ;

int high[T_MAX], low[T_MAX];

void pushUp(int rt)
{
    int lRt = (rt*), rRt = lRt+;
    high[rt] = max(high[lRt], high[rRt]);
    low[rt] = min(low[lRt], low[rRt]);
}
//void initail(int rt, int l, int r)
//{
//    if(l == r)
//    {
//        high[rt] = ans_m[l];
//        low[rt] = high[rt];
//        //printf("---%d---  %d  ss --- %d\n", l, ans_m[l], ans_s[l]);
//    }
//    else
//    {
//        int mid = (l + r)/2;
//        int lRt = (rt*2), rRt = lRt+1;
//        initail(lRt, l, mid);
//        initail(rRt, mid + 1, r);
//        pushUp(rt);
//    }
//}
//int query_max(int L, int R, int rt, int l, int r)
//{
//    if(L <= l && R >= r)
//    {
//        return high[rt];
//    }
//    int mid = ((l + r)>>1), lRt = (rt<<1), rRt = ((rt<<1)|1);
//    int ans = 0;
//    if(L <= mid)
//    {
//        ans = max(ans, query_max(L, R, lRt, l, mid));
//    }
//    if(R >= mid + 1)
//    {
//        ans = max(ans, query_max(L, R, rRt, mid + 1, r));
//    }
//    return ans;
//}
//int query_min(int L, int R, int rt, int l, int r)
//{
//    if(L <= l && R >= r)
//    {
//        return low[rt];
//    }
//    int mid = (l + r)/2;
//    int lRt = (rt*2), rRt = lRt+1;
//    int ans = INF;
//    if(L <= mid)
//    {
//        ans = min(ans, query_min(L, R, lRt, l, mid));
//    }
//    if(R >= mid + 1)
//    {
//        ans = min(ans, query_min(L, R, rRt, mid + 1, r));
//    }
//    return ans;
//}
int a[maxn];

int dp1[maxn][];
int dp2[maxn][];
int mm[maxn];//mm是代表長度對應的二的幂次,1-0,2-1,3-1,4-2
void initRMQ(int n)
{
    mm[] = -;//0是-1,這樣1就是0了
    for(int i = ; i <= n; i++)//算出mm和初始化dp[i][0]
    {
        mm[i] = ((i&(i-)) == )?mm[i-]+:mm[i-];
        dp1[i][] = a[i];
        dp2[i][] = a[i];
    }
    for(int j = ; j <= mm[n]; j++)//長度為2,4,8...
        for(int i = ; i + (<<j) -  <= n; i++)//枚舉起始為1,2,3....
        {
            dp1[i][j] = max(dp1[i][j-],dp1[i + (<<(j-))][j-]);//起始為i,長度幂次j,那麼就是分成起始i,長度幂次j-1,起始i+(1<<(j-1)),長度幂次j-1.
            dp2[i][j] = min(dp2[i][j-],dp2[i + (<<(j-))][j-]);
        }
}
int rmq(int x,int y)//查詢
{
    int k = mm[y-x+];//首先搞出長度幂次.
    return max(dp1[x][k],dp1[y-(<<k)+][k]) - min(dp2[x][k],dp2[y-(<<k)+][k]);//弄的時候要重複一點保證長度是真的幂次,x長度幂次k,y-(1<<k)+1長度幂次k.
}
void solve()
{
    dfs(, );
    getAns_dfs(, , );
    //initail(1, 1, n);
    REP_D(i, , n)
    {
        a[i] = max(ans[i], dp_m[i]);//這個a就是最終的結果
        //printf("--%d--%d\n", i, a[i]);
    }
    initRMQ(n);//初始化RMQ,使得好查
    while(m--)
    {
        int Q;
        scanf("%d",&Q);
        int ans = ;
        int id = ;//很關鍵,标準的尺寸法,for枚舉右端點,id搞左端點
        for(int i = ; i <= n; i++)
        {
            while(id <= i && rmq(id,i) > Q)id++;//找到對應i的最好的id
            ans = max(ans,i-id+);
        }
        printf("%d\n",ans);
    }


//    while(m--)
//    {
//        int q;
//        int a = 0;
//        scanf("%d", &q);
//        int l = 1, r = 1;
//        while(1)
//        {
//            if(r > n || l > n)
//                break;
//            if(query_max(l, r, 1, 1, n)-query_min(l, r, 1, 1, n)<=q)
//            {
//                a = max(a, r - l + 1);
//                r++;
//            }
//            else
//            {
//                l++;
//            }
//        }
//        printf("%d\n", a);
//    }
}

int main()
{
   // freopen("2Bin.txt", "r", stdin);
    //freopen("3Bout.txt", "w", stdout);
    while(scanf("%d%d", &n, &m) != EOF)
    {
        if(!n&&!m)
        {
            break;
        }
        REP_D(i, , n)
        {
            G[i].clear();
        }
        REP_D(i, , n - )
        {
            int a, b, len;
            scanf("%d%d%d", &a, &b, &len);
            info tmp;
            tmp.to = b;
            tmp.len = len;
            G[a].push_back(tmp);
            tmp.to = a;
            G[b].push_back(tmp);
        }
        solve();
    }
    return ;
}