poj 3162
题目:
给出n个点,每个点之间又一个距离,这n个点构成一颗树形结构的图。
一个人从第1个点出发,跑到离第1个点最远的地方,跑过的距离为x,依次跑完n个点,此时,每个点都有一个最远的距离,得到最远距离序列a。
找出a序列中一段连续的区间,这个连续的区间内的最大值与最小值之差<=m。求出这个区间的最大长度是多少。
思路:
对于每个点i的最长距离,用树形dp求出。
然后不断遍历一段区间[L,R]求出求出这个区间内的最大与最小值,而且满足最大与最小值之差小于m,这个过程用线段树或者
单调队列来维护。
线段树实现:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
const int INF = 1e9+10;
int n,head[maxn],tot,id[maxn] = {0};
ll dp[3][maxn],m;
struct Node
{
int v,nxt,w;
} cur[maxn<<2];
void Init()
{
memset(head,-1,sizeof(head));
tot = 0;
}
void Add(int x,int y,int z)
{
cur[tot].v = y;
cur[tot].nxt = head[x];
cur[tot].w = z;
head[x] = tot++;
}
void dfs1(int rt,int fa)
{
for(int i=head[rt];i!=-1;i=cur[i].nxt)
{
int v = cur[i].v,w = cur[i].w;
if(fa==v) continue;
dfs1(v,rt);
if(dp[0][rt]<dp[0][v]+w){
dp[1][rt] = dp[0][rt];
dp[0][rt] = dp[0][v] + w;
id[rt] = v;
}
else if(dp[1][rt]<dp[0][v]+w)
{
dp[1][rt] = dp[0][v] + w;
}
}
}
void dfs2(int rt,int fa)
{
for(int i=head[rt];i!=-1;i=cur[i].nxt)
{
int v = cur[i].v,w = cur[i].w;
if(fa==v) continue;
if(id[rt]==v)
{
dp[2][v] = max(dp[1][rt],dp[2][rt]) + w;
}
else{
dp[2][v] = max(dp[0][rt],dp[2][rt]) + w;
}
dfs2(v,rt);
}
}
int tmin[maxn<<2],tmax[maxn<<2];
void build(int rt,int l,int r)
{
if(l==r)
{
tmin[rt] = tmax[rt] = max(dp[0][l],dp[2][l]);
return ;
}
int mid = (l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
tmin[rt] = min(tmin[rt<<1],tmin[rt<<1|1]);
tmax[rt] = max(tmax[rt<<1],tmax[rt<<1|1]);
}
int mx,mi;
void query(int rt,int l,int r,int L,int R)
{
if(L<=l&&r<=R)
{
mx = max(mx,tmax[rt]);
mi = min(mi,tmin[rt]);
return ;
}
int mid = (l+r)>>1;
if(L<=mid)
query(rt<<1,l,mid,L,R);
if(R>mid)
query(rt<<1|1,mid+1,r,L,R);
}
int main(void)
{
while(~scanf("%d%d",&n,&m))
{
Init();
dp[0][1] = dp[1][1] = dp[2][1] = 0;
id[1] = -1;
for(int i=2; i<=n; i++)
{
int y,z;
scanf("%d%d",&y,&z);
Add(i,y,z);
Add(y,i,z);
dp[0][i] = dp[1][i] = dp[2][i] = 0;
id[i] = -1;
}
dfs1(1,-1);
dfs2(1,-1);
build(1,1,n);
int l = 1,r = 1,ans = 0;
while(r<=n)
{
mi = INF;
mx = 0;
query(1,1,n,l,r);
if(mx-mi<=m)
{
ans = max(ans,(r-l+1));
r++;
}
while(mx-mi>m)
{
l++;
mi = INF;
mx = 0;
query(1,1,n,l,r);
}
}
printf("%d\n",ans);
}
return 0;
}
单调队列:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 1e6+10;
int dp[3][maxn],n,head[maxn],tot,m,id[maxn];
int que1[maxn],que2[maxn],top1,rear1,top2,rear2;
struct Node
{
int v,nxt,w;
}cur[maxn<<2];
void Init()
{
memset(head,-1,sizeof(head));
tot = 0;
}
void Add(int x,int y,int z)
{
cur[tot].v = y;
cur[tot].nxt = head[x];
cur[tot].w = z;
head[x] = tot++;
}
void dfs1(int rt,int fa)
{
for(int i=head[rt];i!=-1;i=cur[i].nxt)
{
int v = cur[i].v;
if(fa==v) continue;
dfs1(v,rt);
if(dp[0][rt]<dp[0][v]+cur[i].w)
{
dp[1][rt] = dp[0][rt];
dp[0][rt] = dp[0][v] + cur[i].w;
id[rt] = v;
}
else if(dp[1][rt]<dp[0][v] + cur[i].w)
{
dp[1][rt] = dp[0][v] + cur[i].w;
}
}
}
void dfs2(int rt,int fa)
{
for(int i=head[rt];i!=-1;i=cur[i].nxt)
{
int v = cur[i].v;
if(fa==v) continue;
if(id[rt]==v)
{
dp[2][v] = max(dp[2][rt],dp[1][rt]) + cur[i].w;
}
else
{
dp[2][v] = max(dp[2][rt],dp[0][rt]) + cur[i].w;
}
dfs2(v,rt);
}
}
int main(void)
{
while(~scanf("%d%d",&n,&m))
{
Init();
dp[0][1] = dp[1][1] = dp[2][1] = 0;
id[1] = 0;
for(int i=2;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
Add(x,i,y);
Add(i,x,y);
dp[0][i] = dp[1][i] = dp[2][i] = 0;
id[i] = 0;
}
dfs1(1,0);
dfs2(1,0);
top1 = rear1 = 1;
top2 = rear2 = 1;
int ans = -1;
for(int i=1,j=1;i<=n;i++)
{
dp[0][i] = max(dp[0][i],dp[2][i]);
//注意底下的入队操作。
while(top1<rear1&&dp[0][ que1[rear1-1] ]<dp[0][i]) rear1--; //mx,保持队列内第一个最大
que1[rear1++] = i;
while(top2<rear2&&dp[0][ que2[rear2-1] ]>dp[0][i]) rear2--; //mi,保持队列内第一个最小
que2[rear2++] = i;
while(top1<rear1&&top2<rear2&&( dp[0][ que1[top1] ] - dp[0][ que2[top2] ] > m)){
if(que2[top2] < que1[top1]) j=que2[top2]+1,top2++;
else j=que1[top1]+1,top1++;
}
ans = max(ans,(i-j+1));
}
printf("%d\n",ans);
}
return 0;
}