您的位置:首页 > 其它

BSOJ3068 BZOJ2500 noip模拟赛 幸福的道路 树的最长链+单调队列 或 RMQ

2016-09-28 22:18 417 查看
3068 -- 【模拟试题】幸福的道路
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
Hint
【数据范围】
  50%的数据N<=1000
  80%的数据N<=100000
  100%的数据N<=200000
题解:
First

    1. 双树形DP 

    2. 树的直径

Second

    1. 二分加线性扫描 (非常好想)

    2. 二分加ST (很好想)
    3. 双单调队列  (比较能想)

树的直径有两种求法,每次都直接DFS两次是过不了的,只有用两次树形DP,
g[x][0]表示从x的子树中,x到叶子的最长链,g[x][1]表示次长链。(用儿子更新父亲)
f[x]表示从x向上走到某个父亲,再向下的最长链。(用父亲更新儿子)
这个DP是通过两次从根出发的dfs实现的。
第二次的处理可以用单调队列或者二分+RMQ通过
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
using namespace std;
struct node{int to,nxt,val;
}w[400005];
int cnt=0,h[400005],g[200005][2],f[200005],n,m,a[200005],q1[200005],q2[200005];
void add(int x,int y,int z)
{
cnt++;w[cnt].to=y;w[cnt].nxt=h[x];w[cnt].val=z;h[x]=cnt;
}
void DP1(int pos,int fa)
{
for(int i=h[pos];i;i=w[i].nxt)
{
int j=w[i].to;
if(j==fa)continue;
DP1(j,pos);
if(w[i].val+g[j][0]>g[pos][0])
{
g[pos][1]=g[pos][0];
g[pos][0]=w[i].val+g[j][0];
}
else g[pos][1]=max(g[pos][1],g[j][0]+w[i].val);
}
return ;
}
void DP2(int pos,int fa)
{
for(int i=h[pos];i;i=w[i].nxt)
{
int j=w[i].to;
if(j==fa)continue;
f[j]=f[pos]+w[i].val;
if(g[j][0]+w[i].val==g[pos][0])f[j]=max(f[j],g[pos][1]+w[i].val);
else f[j]=max(f[j],g[pos][0]+w[i].val);
DP2(j,pos);
}
}

void solve()
{
int l1=1,l2=1,r1=0,r2=0,t=1,ans=0;
for(int i=1;i<=n;i++)
{
while(l1<=r1&&a[i]<=a[q1[r1]]) r1--;  //small to big
while(l2<=r2&&a[i]>=a[q2[r2]]) r2--;  //big to small
q1[++r1]=i;q2[++r2]=i;
while(a[q2[l2]]-a[q1[l1]]>m)
{
if(q2[l2]<=q1[l1]) t=q2[l2]+1,l2++;
else t=q1[l1]+1,l1++;
}
ans=max(ans,i-t+1);
}
cout<<ans;
}
int main(){
int fa,v;
cin>>n>>m;
for(int i=2;i<=n;i++)
{
cin>>fa>>v;
add(i,fa,v);
add(fa,i,v);
}
DP1(1,0);DP2(1,0);
for(int i=1;i<=n;i++)
a[i]=max(f[i],g[i][0]);
solve();
return 0;
}
二分+RMQ要慢一些
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
using namespace std;
struct node
{
int to,next,val;
}w[1000005];
int h[1000005]={0},a[1000005]={0},dp1[1000005][2]={0},dp2[1000005]={0};
int maxx[200005][21]={0},minn[200005][21];
int n,m,cnt=0;
void Addarc(int x,int y,int val)
{
cnt++;w[cnt].to=y;w[cnt].next=h[x];h[x]=cnt;w[cnt].val=val;
}
void DP1(int x,int fa)
{
for(int i=h[x];i;i=w[i].next)
{
int to=w[i].to;
if(to==fa)continue;
DP1(to,x);
if(dp1[to][0]+w[i].val>dp1[x][0])
{
dp1[x][1]=dp1[x][0];
dp1[x][0]=dp1[to][0]+w[i].val;
}
else if(dp1[to][0]+w[i].val>dp1[x][1])dp1[x][1]=dp1[to][0]+w[i].val;
}
}
void DP2(int x,int fa)
{
for(int i=h[x];i;i=w[i].next)
{
int to=w[i].to;
if(to==fa)continue;
dp2[to]=dp2[x]+w[i].val;
if(dp1[to][0]+w[i].val==dp1[x][0])dp2[to]=max(dp2[to],w[i].val+dp1[x][1]);
else dp2[to]=max(dp2[to],w[i].val+dp1[x][0]);
DP2(to,x);
}
}
void ST()
{
memset(minn,127,sizeof(minn));
for(int i=1;i<=n;i++)maxx[i][0]=minn[i][0]=a[i];
for(int j=1;j<=log2(n);j++)
{
for(int i=1;i+(1<<j)-1<=n;i++)
{
maxx[i][j]=max(maxx[i][j-1],maxx[i+(1<<(j-1))][j-1]);
minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);
}
}
}
int ask_mx(int l,int r)
{
int k=log2(r-l+1);
return max(maxx[l][k],maxx[r-(1<<k)+1][k]);
}
int ask_mi(int l,int r)
{
int k=log2(r-l+1);
return min(minn[l][k],minn[r-(1<<k)+1][k]);
}
void init()
{
scanf("%d%d",&n,&m);
for(int i=2;i<=n;i++)
{
int fa,d;
scanf("%d%d",&fa,&d);
Addarc(fa,i,d);
Addarc(i,fa,d);
}
DP1(1,0);
DP2(1,0);
for(int i=1;i<=n;i++)a[i]=max(dp1[i][0],dp2[i]);
ST();
}
bool check(int len)
{
for(int st=1;st+len-1<=n;st++)
{
int ed=st+len-1;
if(ask_mx(st,ed)-ask_mi(st,ed)<=m)return 1;
}
return 0;
}
void Binary_Search()
{
int l=1,r=n,ans=1;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))
{
l=mid+1;
ans=mid;
}
else r=mid-1;
}
printf("%d\n",ans);
}
int main()
{
init();
Binary_Search();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: