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通过
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; }
相关文章推荐
- bzoj2500幸福的道路 树形dp+单调队列
- bzoj 2500 幸福的道路 dfs 单调队列
- 【bzoj2500】【幸福的道路】【树形dp+单调队列】
- 【bzoj2500】幸福的道路 单调队列+树形dp
- 幸福的道路(race) ssl 2570 bzoj 2500 单调队列
- 幸福的道路_bzoj2500_树形dp+单调队列
- BZOJ 2500 幸福的道路 树形DP+单调队列
- [BZOJ 2500]幸福的道路 树形dp+单调队列+二分答案
- bzoj 2500: 幸福的道路 动态规划+单调栈
- 【bzoj2500】幸福的道路 树形dp+倍增RMQ+二分
- 【BZOJ】【2500】幸福的道路
- [Bzoj2500]幸福的道路(树上最远点)
- ●BZOJ 2500 幸福的道路
- 【BZOJ2500】幸福的道路 树形DP+RMQ+双指针法
- bzoj1999 [Noip2007]Core树网的核(树的直径+单调队列+贪心)
- noip2009 道路游戏 (单调队列优化动态规划)
- NOIP模拟赛 军训(二分答案+单调队列优化DP)
- 10.11 NOIP模拟赛 DP + 线段树 + DP + 单调队列
- BZOJ_1999_[Noip2007]Core树网的核_单调队列+树形DP
- bzoj 2500: 幸福的道路