POJ 3162 Walking Race(树形DP + 单调队列)
2014-11-10 21:55
363 查看
题目链接:http://poj.org/problem?id=3162
题意:对一棵树,求出从每个结点出发能到走的最长距离(每个结点最多只能走一次),将这些距离按排成一个数组得到d[1],d[2],d[3]……d
,在数列的d中求一个最长的区间,使得区间中的最大值与最小值的差不超过m。
思路:先求出树的直径,对于树中任意结点i,到树的直径的2个端点的距离的较大者即为最长距离d[i]。得到数组d后,用2个单调队列分别维护最大与最小值,扫描d数组,同时更新答案。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <queue>
#include <map>
#include <set>
using namespace std;
const int maxn = 1000010;
const int maxm = 5000;
const int inf = 0x3f3f3f3f;
int n, m;
int dis1[maxn], dis2[maxn], d[maxn];
int qmin[maxn], qmax[maxn];
struct edge
{
int to, w, nxt;
} e[maxn * 2];
int cnt;
int head[maxn];
void init()
{
cnt = 0;
memset(head, -1, sizeof(head));
}
void add(int u, int v, int w)
{
e[cnt].to = v;
e[cnt].w = w;
e[cnt].nxt = head[u];
head[u] = cnt++;
}
void dfs1(int u)
{
for(int i = head[u]; ~i; i = e[i].nxt)
{
int v = e[i].to;
if(dis1[v] == -1)
{
dis1[v] = dis1[u] + e[i].w;
dfs1(v);
}
}
}
void dfs2(int u)
{
for(int i = head[u]; ~i; i = e[i].nxt)
{
int v = e[i].to;
if(dis2[v] == -1)
{
dis2[v] = dis2[u] + e[i].w;
dfs2(v);
}
}
}
void solve()
{
int ans = 0, i, j;
int f1, r1, f2, r2;
f1 = r1 = 0;
f2 = r2 = 0;
for(i = 1, j = 1; j <= n; j++)
{
while(r1 > f1 && d[qmax[r1 - 1]] <= d[j]) r1--;
qmax[r1++] = j;
while(r2 > f2 && d[qmin[r2 - 1]] >= d[j]) r2--;
qmin[r2++] = j;
if(d[qmax[f1]] - d[qmin[f2]] > m)
{
ans = max(ans, j - i);
while(d[qmax[f1]] - d[qmin[f2]] > m)
{
i = min(qmax[f1], qmin[f2]) + 1;
while(r1 > f1 && qmax[f1] < i) f1++;
while(r2 > f2 && qmin[f2] < i) f2++;
}
}
}
ans = max(ans, j - i);
printf("%d\n", ans);
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
init();
for(int i = 2; i <= n; i++)
{
int v, w;
scanf("%d%d", &v, &w);
add(i, v, w);
add(v, i, w);
}
int len = 0;
int u, v;
memset(dis1, -1, sizeof(dis1));
dis1[1] = 0;
dfs1(1);
for(int i = 1; i <= n; i++)
{
if(dis1[i] > len)
{
len = dis1[i];
u = i;
}
}
memset(dis1, -1, sizeof(dis1));
dis1[u] = 0;
dfs1(u);
for(int i = 1; i <= n; i++)
{
if(dis1[i] > len)
{
len = dis1[i];
v = i;
}
}
memset(dis2, -1, sizeof(dis2));
dis2[v] = 0;
dfs2(v);
for(int i = 1; i <= n; i++)
{
//printf("%d %d\n", dis1[i], dis2[i]);
d[i] = max(dis1[i], dis2[i]);
}
solve();
}
return 0;
}
题意:对一棵树,求出从每个结点出发能到走的最长距离(每个结点最多只能走一次),将这些距离按排成一个数组得到d[1],d[2],d[3]……d
,在数列的d中求一个最长的区间,使得区间中的最大值与最小值的差不超过m。
思路:先求出树的直径,对于树中任意结点i,到树的直径的2个端点的距离的较大者即为最长距离d[i]。得到数组d后,用2个单调队列分别维护最大与最小值,扫描d数组,同时更新答案。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <queue>
#include <map>
#include <set>
using namespace std;
const int maxn = 1000010;
const int maxm = 5000;
const int inf = 0x3f3f3f3f;
int n, m;
int dis1[maxn], dis2[maxn], d[maxn];
int qmin[maxn], qmax[maxn];
struct edge
{
int to, w, nxt;
} e[maxn * 2];
int cnt;
int head[maxn];
void init()
{
cnt = 0;
memset(head, -1, sizeof(head));
}
void add(int u, int v, int w)
{
e[cnt].to = v;
e[cnt].w = w;
e[cnt].nxt = head[u];
head[u] = cnt++;
}
void dfs1(int u)
{
for(int i = head[u]; ~i; i = e[i].nxt)
{
int v = e[i].to;
if(dis1[v] == -1)
{
dis1[v] = dis1[u] + e[i].w;
dfs1(v);
}
}
}
void dfs2(int u)
{
for(int i = head[u]; ~i; i = e[i].nxt)
{
int v = e[i].to;
if(dis2[v] == -1)
{
dis2[v] = dis2[u] + e[i].w;
dfs2(v);
}
}
}
void solve()
{
int ans = 0, i, j;
int f1, r1, f2, r2;
f1 = r1 = 0;
f2 = r2 = 0;
for(i = 1, j = 1; j <= n; j++)
{
while(r1 > f1 && d[qmax[r1 - 1]] <= d[j]) r1--;
qmax[r1++] = j;
while(r2 > f2 && d[qmin[r2 - 1]] >= d[j]) r2--;
qmin[r2++] = j;
if(d[qmax[f1]] - d[qmin[f2]] > m)
{
ans = max(ans, j - i);
while(d[qmax[f1]] - d[qmin[f2]] > m)
{
i = min(qmax[f1], qmin[f2]) + 1;
while(r1 > f1 && qmax[f1] < i) f1++;
while(r2 > f2 && qmin[f2] < i) f2++;
}
}
}
ans = max(ans, j - i);
printf("%d\n", ans);
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
init();
for(int i = 2; i <= n; i++)
{
int v, w;
scanf("%d%d", &v, &w);
add(i, v, w);
add(v, i, w);
}
int len = 0;
int u, v;
memset(dis1, -1, sizeof(dis1));
dis1[1] = 0;
dfs1(1);
for(int i = 1; i <= n; i++)
{
if(dis1[i] > len)
{
len = dis1[i];
u = i;
}
}
memset(dis1, -1, sizeof(dis1));
dis1[u] = 0;
dfs1(u);
for(int i = 1; i <= n; i++)
{
if(dis1[i] > len)
{
len = dis1[i];
v = i;
}
}
memset(dis2, -1, sizeof(dis2));
dis2[v] = 0;
dfs2(v);
for(int i = 1; i <= n; i++)
{
//printf("%d %d\n", dis1[i], dis2[i]);
d[i] = max(dis1[i], dis2[i]);
}
solve();
}
return 0;
}
相关文章推荐
- POJ 3162 Walking Race(树形DP+单调队列)
- poj 3162 Walking Race 树形dp + 单调队列
- 【POJ 3162】 Walking Race (树形DP-求树上最长路径问题,+单调队列)
- POJ 3162 Walking Race(树形dp+单调队列 or 线段树)
- POJ3162 Walking Race(树形DP+尺取法+单调队列)
- poj 3162 树形dp+单调队列 很好的题
- POJ 3162 Walking Race TreeDP+双单调队列 ★
- poj 3162 walking race 树形dp 求符合条件区间
- POJ 3162 Walking Race 树的直径加单调队列
- POJ 3162 Walking Race 树的直径+单调队列(其实暴力也可以>_<)
- POJ 3162 Walking Race (树形DP)
- POJ 3162 Walking Race 树形DP
- POJ 3162 Walking Race (树的直径,单调队列)
- poj 3162 树DP+单调队列
- POJ 3162 Walking Race(单调队列或线段树加树型DP)
- poj 3162 Walking Race (dfs+尺取法单调队列)★
- POJ 3162 Walking Race (单调队列)@
- POJ 3162 Walking Race(dfs+单调队列)
- POJ 3162 - Walking Race(树形DP)
- 【树形DP】Poj 3162 Walking Race (DP_树形DP(线段树))