BZOJ - 2500 樹形DP亂搞
阿新 • • 發佈:2018-11-10
題意:給出一棵樹,兩個給給的人在第\(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; }