1. 程式人生 > >BZOJ - 2500 樹形DP亂搞

BZOJ - 2500 樹形DP亂搞

題意:給出一棵樹,兩個給給的人在第\(i\)天會從節點\(i\)沿著最長路徑走,求最長的連續天數\([L,R]\)使得\([L,R]\)為起點的最長路徑極差不超過m

\(1\)\(n\)的最長路經可用樹形DP求解,
\(f[i]\):\(i\)的子樹下到\(i\)的最遠距離
\(g[i]\):\(i\)子樹下除了\(f[i]\)子樹以外的最遠距離
\(h[i]\):除了\(i\)子樹以外到\(i\)的最遠距離

\(h[i]\)從父到兒子的轉移需要判斷\(i\)到底是\(fa\)的最遠距離所在邊還是次遠距離所在邊(可相等),還有直接來自父親以上\(h[fa]\)的轉移

搞完後求極差就用二分+RMQ強行求出來,注意初始化需要f和h的對比

題目簡單但要細心

#include<bits/stdc++.h>
#define rep(i,j,k) for(register int i=j;i<=k;i++)
#define rrep(i,j,k) for(register int i=j;i>=k;i--)
#define erep(i,u) for(register int i=head[u];~i;i=nxt[i])
#define fastIO ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define println(x) printf("%lld\n",(ll)(x))
using namespace std;
typedef long long ll;
const int MAXN = 1e6+11;
const int MOD  = 142857;
const int INF  = 1<<30;

int to[MAXN<<1],nxt[MAXN<<1],head[MAXN],tot;
int cost[MAXN<<1];
int n,m;
void init(int n){memset(head,-1,(n+2)*sizeof(int)),tot=0;}
void add(int u,int v,ll w){
    to[tot]=v;
    cost[tot]=w;
    nxt[tot]=head[u];
    head[u]=tot++;
}
int f[MAXN],g[MAXN],h[MAXN];
int mx[MAXN][22],mn[MAXN][22];
void DP0(int u,int fa){
    f[u]=g[u]=h[u]=0;
    for(int i=head[u];~i;i=nxt[i]){
        int v=to[i]; ll w=cost[i];
        if(v==fa) continue;
        DP0(v,u);
        if(f[v]+w>f[u]){
            g[u]=f[u];   //次長子樹
            f[u]=f[v]+w; //最長子樹
        }else if(f[v]+w>g[u]){
            g[u]=f[v]+w;
        }
    }
}
void DP1(int u,int fa){
    for(int i=head[u];~i;i=nxt[i]){
        int v=to[i]; ll w=cost[i];
        if(v==fa) continue;
        if(f[u]-w==f[v]) h[v]=max(h[u]+w,g[u]+w);//本身v作為兒子是f[u]的最大值,那就從u的次大子樹中轉移
        else h[v]=max(h[u]+w,f[u]+w);
        DP1(v,u);
    }
}
ll C(int lo,int hi){
    int k=log2(hi-lo+1);
    return max(mx[lo][k],mx[hi-(1<<k)+1][k])
          -min(mn[lo][k],mn[hi-(1<<k)+1][k]);
}
int gao(int st){
    int lo=st,hi=n;
    while(lo<hi){
        int mid=lo+(hi-lo+1)/2;
        if(C(st,mid)<=m) lo=mid;
        else hi=mid-1;
    }
    return C(st,lo)?lo:lo-1;
}
int main(){
#ifndef ONLINE_JUDGE
    freopen("stdin.txt","r",stdin);
#endif
    while(~scanf("%d%d",&n,&m)){
        init(n);
        for(int i=2;i<=n;i++){
            int fi;ll di;
            scanf("%d%lld",&fi,&di);
            add(i,fi,di);
            add(fi,i,di);
        }
        DP0(1,-1);
        DP1(1,-1);
        for(int i=1;i<=n;i++){
            mx[i][0]=mn[i][0]=max(f[i],h[i]);//f[]只考慮子樹內,h[]只考慮子樹外
        }
        int t=log2(n);
        for(int i=1;i<=t;i++){
            for(int j=1;j<=n;j++){
                mx[j][i]=max(mx[j][i-1],mx[j+(1<<i-1)][i-1]);
                mn[j][i]=min(mn[j][i-1],mn[j+(1<<i-1)][i-1]);
            }
        }
        int ans=0;
        for(int i=1;i<=n;i++){
            int hi=gao(i);
            ans=max(ans,hi-i+1);
        }
        println(ans);
    }
    return 0;
}