POJ 3162 Walking Race 树形DP+线段树

给出一棵树,编号为1~n,给出数m

漂亮mm连续n天锻炼身体,每天会以节点i为起点,走到离i最远距离的节点

走了n天之后,mm想到知道自己这n天的锻炼效果

于是mm把这n天每一天走的距离记录在一起,成为一段长度为n的数列

现在mm想要从这数列中选出一个连续的区间,要求这个区间的max-min<=m

输出最长的区间

做了一个下午

思路:

分成2个部分:

1.求出数列,即对于一棵树,求出每一个节点能到达的最远距离

2.对于这段数列,选出一个区间,使得区间的max-min<=m,并且使得区间长度尽量长。

对于第1部分,其实就是我昨天做的题目,HDU2196,使用树形DP

dp[i][1]:表示从i出发到i的子树,能到达的最远距离

dp[i][2]:表示从i出发到i的子树,能到达的第二远的距离(求dp[i][0]的时候需要用到)

dp[i][0]:表示从i出发,经过i的父亲节点,能到达的最远距离

4次dfs,分别求出:

0.节点的depth,以便把边权(u,v)转化为u,v中depth较大的节点的点权,cost[root]=0;

1.求出dp[i][1]和son[i],son[i]表示dp[i][1]是通过子节点son[i]这道路径得到的

2.求出dp[i][2]

3.求出dp[i][0]

完成第一部分

第二部分,前段时间也做过类似的题,HDU5289

5289要求的是数列中有多少个区间满足条件,我是用RMQ+二分

这道求的是满足条件的最长区间长度

我主要时间是花在了解决第二部分

我刚开始也是用RMQ+二分

发现mle了

因为5289的长度n<=1e5,而这道,n<=1e6,所以RMQ开了会mle

然后我就想改为用线段树代替RMQ的作用,用线段树维护区间的最大值和最小值也很方便,还可以省下很多空间

然后,tle了

原因:

这道题和5289不同的是,这道题不用枚举每一个区间,判断到底满不满足要求

只要求出一个满足的最长的就好啦

然后把枚举左端点+二分右端点的代码注释了

换了种方式:

用2个指针l,r,

若区间[l,r]满足条件,r++,同时更新答案

若不满足条件,l++,直到[l,r]满足条件

复杂度O(n)

这就是传说中的尺取法?

这过程我还犯了一个及其严重的错误:

求最大值的时候,没有把max_ret初始化为-inf

求最小值的时候,没有把min_ret初始化为inf

又花了不少时间

然后终于过了

  1 #include<cstdio>
  2 #include<cstring>
  3
  4 using namespace std;
  5 #define lson l,m,rt<<1
  6 #define rson m+1,r,rt<<1|1
  7
  8 inline int max(int a,int b)
  9 {
 10     return a>b?a:b;
 11 }
 12
 13 inline int min(int a,int b)
 14 {
 15     return a<b?a:b;
 16 }
 17
 18 const int maxn=1e6+5;
 19 const int inf=0x3f3f3f3f;
 20
 21 struct Edge
 22 {
 23     int to,next;
 24 };
 25 Edge edge[maxn<<1];
 26 int head[maxn];
 27 int tot;
 28 int son[maxn];
 29 int dp[maxn][3];
 30 int e[maxn][2];
 31 int dep[maxn];
 32 int cost[maxn];
 33 int seg_max[maxn<<2];
 34 int seg_min[maxn<<2];
 35 int max_ret;
 36 int min_ret;
 37
 38 void init()
 39 {
 40     memset(head,-1,sizeof head);
 41     tot=1;
 42     memset(dep,0,sizeof dep);
 43     memset(dp,0,sizeof dp);
 44     memset(son,-1,sizeof son);
 45 }
 46
 47 void addedge(int u,int v)
 48 {
 49     edge[tot].to=v;
 50     edge[tot].next=head[u];
 51     head[u]=tot++;
 52 }
 53
 54 void solve(int n,int m);
 55 void dfs0(int u,int pre);
 56 void dfs1(int u,int pre);
 57 void dfs2(int u,int pre);
 58 void dfs3(int u,int pre);
 59 void build(int l,int r,int rt);
 60 void pushup(int rt);
 61 int query(int L,int R,int n);
 62 void query_ret(int L,int R,int l,int r,int rt);
 63
 64 int main()
 65 {
 66     int n,m;
 67     while(~scanf("%d %d",&n,&m))
 68     {
 69         init();
 70         for(int i=2;i<=n;i++)
 71         {
 72             int u,v;
 73             scanf("%d %d",&e[i][0],&e[i][1]);
 74             addedge(i,e[i][0]);
 75             addedge(e[i][0],i);
 76         }
 77         solve(n,m);
 78     }
 79     return 0;
 80 }
 81
 82 void solve(int n,int m)
 83 {
 84     dfs0(1,-1);
 85     cost[1]=0;
 86     for(int i=2;i<=n;i++)
 87     {
 88         if(dep[i]>dep[e[i][0]])
 89             cost[i]=e[i][1];
 90         else
 91             cost[e[i][0]]=e[i][1];
 92     }
 93     dfs1(1,-1);
 94     dfs2(1,-1);
 95     dfs3(1,-1);
 96     build(1,n,1);
 97     /*
 98     for(int i=1;i<=n;i++)
 99     {
100         printf("%d\n",max(dp[i][0],dp[i][1]));
101     }
102     */
103     /*
104     int ans=0;
105     for(int i=1;i<=n;i++)
106     {
107         int l=i,r=n;
108         while(r-l>1)
109         {
110             int mid=(l+r)>>1;
111             if(query(i,mid,n)>m)
112                 r=mid;
113             else
114                 l=mid;
115         }
116         if(query(i,r,n)<=m)
117             ans=max(ans,r-i+1);
118         else
119             ans=max(ans,l-i+1);
120     }
121     */
122
123     int ans=0;
124     int l=1,r=1;
125     while(r<=n)
126     {
127         while(l<=r&&query(l,r,n)>m)
128         {
129             l++;
130         }
131         while(r<=n&&query(l,r,n)<=m)
132         {
133             ans=max(ans,r-l+1);
134             r++;
135         }
136     }
137
138     printf("%d\n",ans);
139     return ;
140 }
141
142 void dfs0(int u,int pre)
143 {
144     for(int i=head[u];~i;i=edge[i].next)
145     {
146         int v=edge[i].to;
147         if(v==pre)
148             continue;
149         dep[v]=dep[u]+1;
150         dfs0(v,u);
151     }
152 }
153
154 void dfs1(int u,int pre)
155 {
156     for(int i=head[u];~i;i=edge[i].next)
157     {
158         int v=edge[i].to;
159         if(v==pre)
160             continue;
161         dfs1(v,u);
162         if(dp[v][1]+cost[v]>dp[u][1])
163         {
164             dp[u][1]=dp[v][1]+cost[v];
165             son[u]=v;
166         }
167     }
168 }
169
170 void dfs2(int u,int pre)
171 {
172     for(int i=head[u];~i;i=edge[i].next)
173     {
174         int v=edge[i].to;
175         if(v==pre)
176             continue;
177         dfs2(v,u);
178         if(v==son[u])
179             continue;
180         if(dp[v][1]+cost[v]>dp[u][2])
181         {
182             dp[u][2]=dp[v][1]+cost[v];
183         }
184     }
185 }
186
187 void dfs3(int u,int pre)
188 {
189     for(int i=head[u];~i;i=edge[i].next)
190     {
191         int v=edge[i].to;
192         if(v==pre)
193             continue;
194         if(v==son[u])
195         {
196             dp[v][0]=max(dp[u][0],dp[u][2])+cost[v];
197         }
198         else
199         {
200             dp[v][0]=max(dp[u][0],dp[u][1])+cost[v];
201         }
202         dfs3(v,u);
203     }
204 }
205
206 void pushup(int rt)
207 {
208     seg_max[rt]=max(seg_max[rt<<1],seg_max[rt<<1|1]);
209     seg_min[rt]=min(seg_min[rt<<1],seg_min[rt<<1|1]);
210 }
211
212 void build(int l,int r,int rt)
213 {
214     if(l==r)
215     {
216         seg_max[rt]=seg_min[rt]=max(dp[l][0],dp[l][1]);
217         return ;
218     }
219     int m=(l+r)>>1;
220     build(lson);
221     build(rson);
222     pushup(rt);
223 }
224
225 int query(int L,int R,int n)
226 {
227     max_ret=-inf;
228     min_ret=inf;
229     query_ret(L,R,1,n,1);
230     return max_ret-min_ret;
231 }
232
233 void query_ret(int L,int R,int l,int r,int rt)
234 {
235     if(L<=l&&R>=r)
236     {
237         max_ret=max(max_ret,seg_max[rt]);
238         min_ret=min(min_ret,seg_min[rt]);
239         return ;
240     }
241     int m=(l+r)>>1;
242     if(L<=m)
243         query_ret(L,R,lson);
244     if(R>m)
245         query_ret(L,R,rson);
246 }

时间: 2025-01-10 02:48:48

POJ 3162 Walking Race 树形DP+线段树的相关文章

POJ - 3162 Walking Race 树形dp 单调队列

POJ - 3162Walking Race 题目大意:有n个训练点,第i天就选择第i个训练点为起点跑到最远距离的点,然后连续的几天里如果最远距离的最大值和最小值的差距不超过m就可以作为观测区间,问这样的区间最长的长度? 一开始楞是没看懂题意,最讨厌这种四级题,这是在刁难我英语小能手(能用翻译的就不自己动手).而且这题感觉单调队列那里的处理更难一点,不过还是来说一说怎么树形dp取得最远距离,先画个简简单单丑丑的图 我们直接从1作为根节点开始dfs的话,可以处理1的最远距离,并且可以得出到其它节点

POJ 3162 Walking Race 树形dp 优先队列

http://poj.org/problem?id=3162 题意 :  一棵n个节点的树.wc爱跑步,跑n天,第i天从第i个节点开始跑步,每次跑到距第i个节点最远的那个节点(产生了n个距离),现在要在这n个距离里取连续的若干天,使得这些天里最大距离和最小距离的差小于M,问怎么取使得天数最多? 每个点的最大距离和之前http://acm.hdu.edu.cn/showproblem.php?pid=2196这道题一样 (就是求每个子树的最长子链,次长子链,然后求经过父亲节点能达到的最大值,比较一

hdu5293 Tree chain problem 树形dp+线段树

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5293 在一棵树中,给出若干条链和链的权值.求选取不相交的链使得权值和最大. 比赛的时候以为是树链剖分就果断没去想,事实上是没思路. 看了题解,原来是树形dp.话说多校第一场树形dp还真多. . .. 维护d[i],表示以i为根节点的子树的最优答案. sum[i]表示i的儿子节点(仅仅能是儿子节点)的d值和. 那么答案就是d[root]. 怎样更新d值 d[i] = max(sum[i] , w[p]+s

Codeforces 671D. Roads in Yusland(树形DP+线段树)

调了半天居然还能是线段树写错了,药丸 这题大概是类似一个树形DP的东西.设$dp[i]$为修完i这棵子树的最小代价,假设当前点为$x$,但是转移的时候我们不知道子节点到底有没有一条越过$x$的路.如果我们枚举每条路去转移,会发现这条路沿线上的其他子树的答案难以统计,那怎么办呢,我们可以让这条路向上回溯的时候顺便记录一下,于是有$val[i]$表示必修i这条路,并且修完当前子树的最小代价. 则有转移$dp[x]=min(val[j])$,且$j$这条路必须覆盖$x$. $val[i]=(\sum

poj3162(树形dp+线段树求最大最小值)

题目链接:https://vjudge.net/problem/POJ-3162 题意:给一棵树,求每个结点的树上最远距离,记为a[i],然后求最大区间[l,r]满足区间内的max(a[i])-min(a[i])<=M. 思路:第一步向hdoj2196那题一样树形dp求出每个结点的最长距离,我的另一篇博客中有写到https://www.cnblogs.com/FrankChen831X/p/11375572.html.求出最远距离a[i]后,建立线段树维护区间的最大最小值.然后用两个指针i,j遍

POJ 3162 Walking Race (树的直径,单调队列)

题意:给定一棵带边权的n个节点的树,首先要求出每个点的最长路,然后写成序列d[1],d[2]...d[n],然后求满足 区间最大值-区间最小值<=k 的最大区间长度为多少? 思路: 分两步进行: (1)最多仅需3次DFS就可以在O(n)时间内求出每个点的最长路了.先从任意点t出发遍历过有点,记录下从点t出发到每个点的最长路,然后从记录的其中一个距t最远的点root出发,再一次DFS,就可以得到离root最远的点e啦,再从e出发DFS一次,就得到所有点的最长路了.注意3次DFS的代码都是一样的喔~

POJ 3162 Walking Race(树的直径+单调队列)

题目大意:对一棵树,求出从每个结点出发能到走的最长距离(每个结点最多只能经过一次),将这些距离按排成一个数组得到dis[1],dis[2],dis[3]……dis[n] ,在数列的dis中求一个最长的区间,使得区间中的最大值与最小值的差不超过m. 思路:先找出树的直径的两个端点来,那么树当中的其它节点能走的最大距离一定是到这个两个点当中的其中一个的.所以三遍bfs就可以求出来这个最远的距离,那么,这个最远的距离求出来之后再用两个单调队列来维护最大值和最小值. /*****************

Codeforces Round #530 (Div. 2) E (树形dp+线段树)

链接: 题意: 给你一棵树,树上有n个节点,每个节点上有ai块饼干,在这个节点上的每块饼干需要花费bi时间,有两个玩家,玩家一可以移动到当前点的子节点也可以申请游戏结束返回根节点并吃沿途的饼干,玩家二可以删除当前点到儿子节点的一条边,走路和吃饼干都消耗时间,会给出一个总时间,在总时间内尽可能的多吃饼干,问最多能吃多少个? 思路: 由于是玩家一先手,那么最开始的最大边则不会被删除,但之后路途的最大边都会被玩家二删除,所以我们对于当前点我们需要求: 1.如果现在回头那么最多可以吃到多少饼干 2.向下

Codeforces Round #530 (Div. 2)F Cookies (树形dp+线段树)

题:https://codeforces.com/contest/1099/problem/F 题意:给定一个树,每个节点有俩个信息x和t,分别表示这个节点上的饼干个数和先手吃掉这个节点上一个饼干的的时间.然后有先手和后手俩个人. ?先手可以这么操作:在规定总时间T到达某个节点然后一定要返回根节点1,期间可以选择吃掉某些节点上的某些饼干(前提是保证剩下的时间能够回到根节点): ?后手可以这么操作:在先手到达的位置和这个位置的孩子之间的连边选择一条让先手吃得更多的边摧毁掉,也可以跳过这个过程: 问