1. 程式人生 > >[BZOJ]2500: 幸福的道路

[BZOJ]2500: 幸福的道路

整數 include 我們 最小 desc space content 每次 表示

題解: 題目分為兩問 求從每個點出發的最長鏈

          求一段連續最長的區間讓極差小於m

對於第一問 顯然是樹dp 我們考慮對於一個點的最長路徑要麽從父親出發 要麽是從兒子出發 這就是我們所謂的 "上搞搞下搞搞" 很顯然的我們只需要維護從父親走的最長路和從兒子走的最長路取max即可 我們設從兒子走的最長路徑為maxx 從父親走的最長路徑為fmaxx 但是存在一種情況 設 y是x的父親節點 它存在 maxx[y]=maxx[x]+x到y的邊權 但這樣的話你去更新fmaxx[x]時 就不能用最長兒子路徑去更新 因為子樹路徑會走重復 所以我們用次大子樹路徑去更新最大兒子路徑的fmaxx 總之我們需要維護三個 變量 從父親走的最長路徑 fmaxx 從兒子走的最長路徑 maxx 從兒子走的次長路徑 cmaxx

對於第二問 單調隊列是可以直接做的(但是基本沒寫過單調隊列) 我寫的是倍增ST表 雙指針掃過去

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <queue>
#include <cmath>
#include <set>
#include <map>
#define mp make_pair
#define pb push_back
#define pii pair<int,int>
#define link(x) for(edge *j=h[x];j;j=j->next)
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
#define ll long long
const int MAXN=1e6+10;
const double eps=1e-8;
using namespace std;
struct edge{int t,v;edge*next;}e[MAXN],*h[MAXN],*o=e;
void add(int x,int y,int vul){o->t=y;o->v=vul;o->next=h[x];h[x]=o++;}
ll read(){
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch==‘-‘)f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-‘0‘,ch=getchar();
    return x*f;
}

int maxx[MAXN],cmaxx[MAXN],a[MAXN];
void dfs1(int x,int fa){
    link(x){
	dfs1(j->t,x);
	if(maxx[x]<maxx[j->t]+j->v)cmaxx[x]=maxx[x],maxx[x]=maxx[j->t]+j->v;
	else if(cmaxx[x]<maxx[j->t]+j->v)cmaxx[x]=maxx[j->t]+j->v;
    }
}

int fmaxx[MAXN];
void dfs2(int x,int fa){
    a[x]=max(maxx[x],fmaxx[x]);
    link(x){
	int temp=(maxx[x]==maxx[j->t]+j->v?cmaxx[x]:maxx[x]);
	temp=max(temp,fmaxx[x]);
	fmaxx[j->t]=temp+j->v;
	dfs2(j->t,x);
    }
}

int dp1[MAXN][21],dp2[MAXN][21];
int ma[MAXN];

int get_max(int l,int r){
    int k=r-l+1;
    k=ma[k];
    return max(dp1[l][k],dp1[r-(1<<k)+1][k]); 
}

int get_min(int l,int r){
    int k=r-l+1;
    k=ma[k];
    return min(dp2[l][k],dp2[r-(1<<k)+1][k]);
}
int n;int m;
bool check(int l,int r){
    if(get_max(l,r)-get_min(l,r)<=m)return 1;
    return 0;
}

int main(){
    ma[1]=ma[0]=0;
    inc(i,2,MAXN-1)ma[i]=ma[i/2]+1;
    n=read();m=read();
    int u;int k;
    inc(i,2,n)u=read(),k=read(),add(u,i,k);
    dfs1(1,0);dfs2(1,0);
    inc(i,1,n)dp1[i][0]=dp2[i][0]=a[i];
    inc(j,1,20){
	for(int i=1;i+(1<<j)<=n+1;i++){
	    dp1[i][j]=max(dp1[i][j-1],dp1[i+(1<<(j-1))][j-1]);
	    dp2[i][j]=min(dp2[i][j-1],dp2[i+(1<<(j-1))][j-1]);
	}
    }
    int tot=1;int ans=0;
    inc(i,1,n){
	while(tot<=n&&check(i,tot))tot++;
	ans=max(ans,tot-i);
    }
    printf("%d\n",ans);
}

  

2500: 幸福的道路

Time Limit: 20 Sec Memory Limit: 256 MB
Submit: 535 Solved: 219
[Submit][Status][Discuss]

Description

小T與小L終於決定走在一起,他們不想浪費在一起的每一分每一秒,所以他們決定每天早上一同晨練來享受在一起的時光. 他們畫出了晨練路線的草圖,眼尖的小T發現可以用樹來描繪這個草圖. 他們不願枯燥的每天從同一個地方開始他們的鍛煉,所以他們準備給起點標號後順序地從每個起點開始(第一天從起點一開始,第二天從起點二開始……). 而且他們給每條道路定上一個幸福的值.很顯然他們每次出發都想走幸福值和最長的路線(即從起點到樹上的某一點路徑中最長的一條). 他們不願再經歷之前的大起大落,所以決定連續幾天的幸福值波動不能超過M(即一段連續的區間並且區間的最大值最小值之差不超過M).他們想知道要是這樣的話他們最多能連續鍛煉多少天(hint:不一定從第一天一直開始連續鍛煉)? 現在,他們把這個艱巨的任務交給你了!

Input

第一行包含兩個整數N, M(M<=10^9). 第二至第N行,每行兩個數字Fi , Di, 第i行表示第i個節點的父親是Fi,且道路的幸福值是Di.

Output

最長的連續鍛煉天數

Sample Input

3 2
1 1
1 3

Sample Output

3
數據範圍:
50%的數據N<=1000
80%的數據N<=100 000
100%的數據N<=1000 000

[BZOJ]2500: 幸福的道路