Bob’s Race

Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2833    Accepted Submission(s): 917

Problem Description
Bob wants to hold a race to encourage people to do sports. He has got trouble in choosing the route. There are N houses and N - 1 roads in his village. Each road connects two houses, and all houses are connected together. To make the race more interesting, he requires that every participant must start from a different house and run AS FAR AS POSSIBLE without passing a road more than once. The distance difference between the one who runs the longest distance and the one who runs the shortest distance is called “race difference” by Bob. Bob does not want the “race difference”to be more than Q. The houses are numbered from 1 to N. Bob wants that the No. of all starting house must be consecutive. He is now asking you for help. He wants to know the maximum number of starting houses he can choose, by other words, the maximum number of people who can take part in his race.
 
Input
There are several test cases.
The first line of each test case contains two integers N and M. N is the number of houses, M is the number of queries.
The following N-1 lines, each contains three integers, x, y and z, indicating that there is a road of length z connecting house x and house y.
The following M lines are the queries. Each line contains an integer Q, asking that at most how many people can take part in Bob’s race according to the above mentioned rules and under the condition that the“race difference”is no more than Q.

The input ends with N = 0 and M = 0.

(N<=50000 M<=500 1<=x,y<=N 0<=z<=5000 Q<=10000000)

 
Output
For each test case, you should output the answer in a line for each query.
 
Sample Input
5 5
1 2 3
2 3 4
4 5 3
3 4 2
1
2
3
4
5
0 0
 
Sample Output
1
3
3
3
5
 
Source
 
一、求树的直径问题
     树的直径:树中距离最远的两点间的距离。
     1、树形dp
      首先建树,新学的建树方式,一般我都是直接vector的存点的。
      然后dfs求出每个节点到所有叶子的最大距离。
      最重要的一步,求出跟到除当前子节点之外的所有分支的最大距离,然后加上跟到当前子节点的距离,向上更新。
      先贴一份别人的代码:
      Hdu2196  
#include <stdio.h>
#include <string.h>
#define MAX 10001
#define max(a,b) (a)>(b)?(a):(b) struct node { int v,len,sum;
node *next;
}*head[MAX*],tree[MAX*];
__int64 dp[MAX];
int n,ptr,vis[MAX]; void Initial() { ptr = ;
memset(dp,,sizeof(dp));
memset(vis,,sizeof(vis));
memset(head,NULL,sizeof(head));
}
void AddEdge(int x,int y,int len) { tree[ptr].v = y,tree[ptr].len = len;
tree[ptr].next = head[x],head[x] = &tree[ptr++];
//printf("ptr : %d **** tree[ptr].v: ")
tree[ptr].v = x,tree[ptr].len = len;
tree[ptr].next = head[y],head[y] = &tree[ptr++];
}
void Dfs(int v) { vis[v] = ;
node *p = head[v]; while (p != NULL) { if (!vis[p->v]) { Dfs(p->v);
dp[v] = max(dp[v],dp[p->v]+p->len);
p->sum = dp[p->v] + p->len;
}
p = p->next;
}
}
void Tree_DP(int pa,int son) { if (vis[son]) return;
vis[son] = ;
int i,j,k,maxx = ; node *p = head[pa];
while (p != NULL) {
//找到父节点除son外其他分支的最大价值
if (p->v != son)
maxx = max(maxx,p->sum);
p = p->next;
} p = head[son];
while (p != NULL) { if (p->v == pa) {
//这一步至关重要,往上更新,才能保证每步都得到最优解
p->sum = p->len + maxx;
break;
}
p = p->next;
} p = head[son];
while (p != NULL) {
//每次都更新当前节点,并往下递归计算,父节点会因为vis=1而不计算
dp[son] = max(dp[son],p->sum);
Tree_DP(son,p->v);
p = p->next;
}
} int main()
{
int i,j,k,a,b; while (scanf("%d",&n) != EOF) { Initial();
for (i = ; i <= n; ++i) { scanf("%d%d",&a,&b);
AddEdge(i,a,b);
} Dfs();
memset(vis,,sizeof(vis));
node *p = head[];
while (p != NULL) { Tree_DP(,p->v);
p = p->next;
}
for (i = ; i <= n; ++i)
printf("%I64d\n",dp[i]);
}
}

还有一种其他的理解,这种我感觉比较好理解的。

先建一棵有根数。

求出每个节点到叶子的最长距离和次长距离,并记录到最长距离要经过的子节点(与其相连那一个)。

接下来分两种情况:

(1)当前节点的最长距离经过某子节点,则某子节点的最长距离为当前节点的次长距离和某子节点的最长距离的最大值加当前节点到某子节点的距离

(2)当前节点的最长距离不经过某子节点,则某子节点的最长距离为当前节点的最长距离和某子节点的最长距离的最大值加当前节点到某子节点的距离

更新子节点

hdu2196

#include <cstdio>
#include <iostream>
#include <sstream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;
#define ll long long
#define _cle(m, a) memset(m, a, sizeof(m))
#define repu(i, a, b) for(int i = a; i < b; i++)
#define repd(i, a, b) for(int i = b; i >= a; i--)
#define sfi(n) scanf("%d", &n)
#define sfl(n) scanf("%I64d", &n)
#define pfi(n) printf("%d\n", n)
#define pfl(n) printf("%I64d\n", n)
#define MAXN 1000005 struct Node
{
int v, len;
Node* next;
}*head[MAXN], tree[MAXN * ];
bool vis[MAXN];
int c = ;
int last[MAXN];
ll maxlen[MAXN], smaxlen[MAXN];
ll dp[MAXN];
void init()
{
c = ;
_cle(vis, );
_cle(head, NULL);
_cle(dp, );
_cle(last, );
return ;
} void Add_Edge(int x, int y, int len)
{
tree[c].v = y, tree[c].len = len;
tree[c].next = head[x], head[x] = &tree[c++];
tree[c].v = x, tree[c].len = len;
tree[c].next = head[y], head[y] = &tree[c++];
} void dfs(int fa, int pre)
{
if(vis[fa]) return ;
vis[fa] = ;
Node* p = head[fa];
while(p != NULL)
{
dfs(p -> v, fa);
p = p -> next;
}
int maxn = ;
p = head[fa];
while(p != NULL)
{
if(maxn < p -> len + maxlen[p -> v] && p -> v != pre)
{
maxn = p -> len + maxlen[p -> v];
last[fa] = p -> v;
}
p = p -> next;
}
maxlen[fa] = maxn;
maxn = ;
p = head[fa];
while(p != NULL)
{
if(p -> v != pre && p -> v != last[fa] && maxn < p -> len + maxlen[p -> v])
maxn = p -> len + maxlen[p -> v];
p = p -> next;
}
smaxlen[fa] = maxn;
return ;
} void DP(int fa)
{
vis[fa] = ;
Node* p = head[fa];
while(p != NULL)
{
if(!vis[p -> v])
{
if(last[fa] != p -> v)
dp[p -> v] = max(dp[fa], maxlen[fa]) + p -> len;
else
dp[p -> v] = max(dp[fa], smaxlen[fa]) + p -> len;
DP(p -> v);
}
p = p -> next;
}
return ;
} int main()
{
int n;
while(~sfi(n))
{
init();
int x, y, len;
repu(i, , n + )
{
sfi(y), sfi(len);
Add_Edge(i, y, len);
} dfs(, -);
_cle(vis, );
DP();
repu(i, , n + )
{
// pfl(smaxlen[i]);
pfl(max(maxlen[i], dp[i]));
}
}
return ;
}
 2、利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点。这样就可以首先任取一个点,bfs求出离其最远的点,在用同样的方法求出离这个叶子节点最远的点,此时两点间的距离就是树的直径。 
Poj1985
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int maxn = ;
vector<int> son[maxn], w[maxn];
bool vis[maxn], viss[maxn];
int f[maxn];
int bfs(int root){
int i, j, k;
int ans = root, maxx = ;
queue<int> q;
memset(vis,,sizeof(vis));
memset(f,,sizeof(f));
q.push(root);
vis[root] = ;f[root] = ;viss[root] = ;
while(!q.empty()){
root = q.front();
q.pop();
for(i=;i<son[root].size();i++){
if(vis[son[root][i]]==){
q.push(son[root][i]);
vis[son[root][i]] = ;viss[son[root][i]] = ;
f[son[root][i]] = f[root]+w[root][i];
if(maxx<f[son[root][i]]){
maxx = f[son[root][i]];
ans = son[root][i];
}
}
}
}
return ans;
}
int solve(int root){
int u, v;
u = bfs(root);
v = bfs(u);
return f[v];
}
int main(){
int i, j, k, n, m;
int x1, x2, l, u;
int res;
char opt;
while(~scanf("%d%d",&n,&m)){
for(i=;i<=n;i++){
son[i].clear();
w[i].clear();
}
for(i=;i<m;i++){
scanf("%d%d%d",&x1,&x2,&l);
scanf(" %c",&opt);
son[x1].push_back(x2);w[x1].push_back(l);
son[x2].push_back(x1);w[x2].push_back(l);
}
res = ;
memset(viss,,sizeof(vis));
for(i=;i<=n;i++){
if(viss[i]==){
res = max(res,solve(i));
}
}
printf("%d\n",res);
}
return ;
}

二、单调队列问题

就是单调队列+类似尺取法吧

下面是完整代码:

#include <cstdio>
#include <iostream>
#include <sstream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;
#define ll long long
#define _cle(m, a) memset(m, a, sizeof(m))
#define repu(i, a, b) for(int i = a; i < b; i++)
#define repd(i, a, b) for(int i = b; i >= a; i--)
#define sfi(n) scanf("%d", &n)
#define sfl(n) scanf("%I64d", &n)
#define pfi(n) printf("%d\n", n)
#define pfl(n) printf("%I64d\n", n)
#define MAXN 1000005 struct Node
{
int v, len;
Node* next;
}*head[MAXN], tree[MAXN * ];
bool vis[MAXN];
int c = ;
int last[MAXN];
ll maxlen[MAXN], smaxlen[MAXN];
ll dp[MAXN];
ll p[MAXN];
void init()
{
c = ;
_cle(vis, );
_cle(head, NULL);
_cle(dp, );
_cle(last, );
return ;
} void Add_Edge(int x, int y, int len)
{
tree[c].v = y, tree[c].len = len;
tree[c].next = head[x], head[x] = &tree[c++];
tree[c].v = x, tree[c].len = len;
tree[c].next = head[y], head[y] = &tree[c++];
} void dfs(int fa, int pre)
{
if(vis[fa]) return ;
vis[fa] = ;
Node* p = head[fa];
while(p != NULL)
{
dfs(p -> v, fa);
p = p -> next;
}
int maxn = ;
p = head[fa];
while(p != NULL)
{
if(maxn < p -> len + maxlen[p -> v] && p -> v != pre)
{
maxn = p -> len + maxlen[p -> v];
last[fa] = p -> v;
}
p = p -> next;
}
maxlen[fa] = maxn;
maxn = ;
p = head[fa];
while(p != NULL)
{
if(p -> v != pre && p -> v != last[fa] && maxn < p -> len + maxlen[p -> v])
maxn = p -> len + maxlen[p -> v];
p = p -> next;
}
smaxlen[fa] = maxn;
return ;
} void DP(int fa)
{
vis[fa] = ;
Node* p = head[fa];
while(p != NULL)
{
if(!vis[p -> v])
{
if(last[fa] != p -> v)
dp[p -> v] = max(dp[fa], maxlen[fa]) + p -> len;
else
dp[p -> v] = max(dp[fa], smaxlen[fa]) + p -> len;
DP(p -> v);
}
p = p -> next;
}
return ;
} int main()
{
int n, m;
while(sfi(n), sfi(m), n + m)
{
init();
int x, y, len;
repu(i, , n)
{
sfi(x), sfi(y), sfi(len);
Add_Edge(x, y, len);
} dfs(, -);
_cle(vis, );
DP();
repu(i, , n + ) p[i] = max(maxlen[i], dp[i]);
int Q;
repu(i, , m)
{
scanf("%d", &Q);
ll maxnum = ;
ll maxn = p[];
ll minn = p[];
ll num = ;
int last, maxp, minp;
last = maxp = minp = ;
repu(j, , n + )
{
if(p[j] > maxn)
{
if(p[j] - minn > Q)
{
maxnum = max(maxnum, num);
num = ;
j = min(minp + , maxp + );
maxn = minn = p[j];
maxp = minp = j;
}
else num++, maxn = p[j], maxp = j;
}
else if(p[j] < minn)
{
if(maxn - p[j] > Q)
{
maxnum = max(maxnum, num);
num = ;
j = min(minp + , maxp + );
maxn = minn = p[j];
maxp = minp = j;
}
else num++, minn = p[j], minp = j;
}
else num++;
}
maxnum = max(maxnum, num);
pfl(maxnum);
}
}
return ;
}

HDU 4123(树的直径+单调队列)的更多相关文章

  1. HDU 4123 Bob's Race:树的直径 + 单调队列 + st表

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4123 题意: 给你一棵树,n个节点,每条边有长度. 然后有m个询问,每个询问给定一个q值. 设dis[ ...

  2. HDU 4123 Bob’s Race 树的直径+单调队列

    题意: 给定n个点的带边权树Q个询问. 以下n-1行给出树 以下Q行每行一个数字表示询问. 首先求出dp[N] :dp[i]表示i点距离树上最远点的距离 询问u, 表示求出 dp 数组中最长的连续序列 ...

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

    题目大意:对一棵树,求出从每个结点出发能到走的最长距离(每个结点最多只能经过一次),将这些距离按排成一个数组得到dis[1],dis[2],dis[3]……dis[n] ,在数列的dis中求一个最长的 ...

  4. bzoj 1999: [Noip2007]Core树网的核【树的直径+单调队列】

    我要懒死了,所以依然是lyd的课件截图 注意是min{max(max(d[uk]),dis(u1,ui),dis(uj,un))},每次都从这三个的max里取min #include<iostr ...

  5. HDU - 5289:Assignment(单调队列||二分+RMQ||二分+线段树)

    Tom owns a company and he is the boss. There are n staffs which are numbered from 1 to n in this com ...

  6. hdu 5945 Fxx and game(单调队列优化DP)

    题目链接:hdu 5945 Fxx and game 题意: 让你从x走到1的位置,问你最小的步数,给你两种走的方式,1.如果k整除x,那么你可以从x走一步到k.2.你可以从x走到j,j+t<= ...

  7. hdu 3410 Passing the Message(单调队列)

    题目链接:hdu 3410 Passing the Message 题意: 说那么多,其实就是对于每个a[i],让你找他的从左边(右边)开始找a[j]<a[i]并且a[j]=max(a[j])( ...

  8. 大视野 1012: [JSOI2008]最大数maxnumber(线段树/ 树状数组/ 单调队列/ 单调栈/ rmq)

    1012: [JSOI2008]最大数maxnumber Time Limit: 3 Sec  Memory Limit: 162 MBSubmit: 9851  Solved: 4318[Submi ...

  9. BZOJ 1396:识别子串 SA+树状数组+单调队列

    1396: 识别子串 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 381  Solved: 243[Submit][Status][Discuss] ...

随机推荐

  1. 8.Methods(一)

    1.Instance Constructors and Classes (Reference Types) Constructors methods : 1.allow an instance of ...

  2. Windows Internals学习笔记(四)Trap Dispatching

    参考资料: 1. <Windows Internals> 知识点: ● 陷阱trap:它是一种处理器机制,用以在某一异常或中断出现时,捕捉该执行线程,并将其控制权转交到操作系统中某一固定位 ...

  3. 2dx中文乱码问题

    我们的代码里面有一个bug 为了跟之前兼容的策划导表工具兼容 导表工具导出的excel全部都是ansi的 为了兼容就只能手动改成utf 8 无bom格式 后来策划嫌烦了 就让在程序段处理这个 研究了好 ...

  4. Sublime Text 2 一些常用的快捷键

    atrl+F查找后按F3或者ctrl+F3,是查找下一个,shift+F3是查找上一个alt+f3高亮所有的查找的ctrl+h 查找并替换 ctrl+left是向左一个单词跳跃,right同理 ctr ...

  5. DZY Loves Chessboard

    DescriptionDZY loves chessboard, and he enjoys playing with it. He has a chessboard of n rows and m ...

  6. mysql概要(十)触发器

    1触发器可监视增删改,并触发增删改操作. 针对一些逻辑关联密切的操作,而不必手动多次操作. 2.要素: 3.语法: 补充:mysql可以设置结束符,不必是“:”来结束标志,可使用 3.1简单的例子: ...

  7. NEU校园网登录器

    http://www.cnblogs.com/weidiao/p/5124106.html 改自学长的博客. 我们的目标是写一个程序实现自动登录校园网.而这基于的是表单的post机制. 输入校园网网址 ...

  8. ubuntu源码安装django

    由于用pip install django方法安装太慢,而且容易报错,故使用源码的方式安装 方法: 下载源码包:https://www.djangoproject.com/download/ 输入以下 ...

  9. JavaScript设计模式-单例模式、模块模式(转载 学习中。。。。)

    (转载地址:http://technicolor.iteye.com/blog/1409656) 之前在<JavaScript小特性-面向对象>里面介绍过JavaScript面向对象的特性 ...

  10. Android控件之CheckBox(复选框控件)

    一.有两种状态: 选中状态(true).未选中状态(false) 二.属性 android:id = "@+id/checkbox" android:layout_width=&q ...