题意就是要求一棵树上的最长不下降序列,同时不下降序列的最小值与最大值不超过D。

  做法是树分治+线段树,假设树根是x,y是其当前需要处理的子树,对于子树y,需要处理出两个数组MN,MX,MN[i]表示以x为第一个数字的不下降子序列中第i个数的最小值,MX[i]表示以x为第一个数字的不上升子序列中第i个数的最大值。如果当前子树有一个以x为首的不下降序列,那么我们就需要在之前处理的子树中找一条以x为首的满足约束条件不上升序列,可以用线段树来查询。同时每做完一颗子树的时候,用MN,MX对线段树进行更新。对于不经过x的情况可以递归下去处理。

  代码:

 #include<cstdio>
#include<algorithm>
#include<cstring>
#define N 1000010
using namespace std;
int dp,p[N],s[N],pre[N],tt[N],flag[N],father[N],tmproot,n,d,ttt;
int i,v[N],a,b,mn,mx,MN[N],MX[N],U,V,ans;
int l[N],r[N],smn[N],smx[N],vsmn[N],vsmx[N];
void build(int x,int a,int b)
{
int m;
l[x]=a;r[x]=b;
if (x>ttt) ttt=x;
if (b-a>)
{
m=(a+b)>>;
build(*x,a,m);
build(*x+,m,b);
}
}
void clean(int x)
{
if (vsmn[x])
{
smn[x]=;
if (*x<=ttt)
vsmn[*x]=;
if (*x+<=ttt)
vsmn[*x+]=;
vsmn[x]=;
} if (vsmx[x])
{
smx[x]=;
if (*x<=ttt)
vsmx[*x]=;
if (*x+<=ttt)
vsmx[*x+]=;
vsmx[x]=;
}
}
void change(int x,int a,int b,int c,int typ)
{
int m;
clean(x);
if ((a<=l[x])&&(r[x]<=b))
{
if (typ==)
smn[x]=max(smn[x],c);
else
smx[x]=max(smx[x],c);
return;
}
m=(l[x]+r[x])>>;
if (a<m) change(*x,a,b,c,typ);
if (m<b) change(*x+,a,b,c,typ);
clean(*x);clean(*x+);
if (!typ)
smn[x]=max(smn[*x],smn[*x+]);
else
smx[x]=max(smx[*x],smx[*x+]);
}
int query(int x,int a,int b,int typ)
{
int m,ans=;
clean(x);
if ((a<=l[x]&&(r[x]<=b)))
{
if (typ==)
return smn[x];
else
return smx[x];
}
m=(l[x]+r[x])>>;
if (a<m) ans=max(ans,query(*x,a,b,typ));
if (m<b) ans=max(ans,query(*x+,a,b,typ));
return ans;
}
void link(int x,int y)
{
dp++;pre[dp]=p[x];p[x]=dp;tt[dp]=y;
}
int getroot(int x,int fa,int sum)
{
int i,f=;
i=p[x];
father[x]=fa;
s[x]=;
while (i)
{
if ((tt[i]!=fa)&&(!flag[tt[i]]))
{
getroot(tt[i],x,sum);
s[x]=s[x]+s[tt[i]];
if (s[tt[i]]>sum/) f=;
}
i=pre[i];
}
if (sum-s[x]>sum/) f=;
if (!f) tmproot=x;
}
void dfs(int x,int fa,int mn,int mx)
{
int i;
i=p[x];
if (v[x]>=v[fa])
{
if (mn)
{
mn++;
MN[mn]=min(MN[mn],v[x]);
}
if (v[x]>v[fa])
mx=;
}
if (v[x]<=v[fa])
{
if (mx)
{
mx++;
MX[mx]=max(MX[mx],v[x]);
}
if (v[x]<v[fa])
mn=;
}
U=max(U,mn);
V=max(V,mx);
while (i)
{
if ((tt[i]!=fa)&&(!flag[tt[i]]))
dfs(tt[i],x,mn,mx);
i=pre[i];
}
}
void work(int x,int sum)
{
int i,j,k,root;
getroot(x,,sum);
root=tmproot;
i=p[root];
flag[root]=;
while (i)
{
if (flag[tt[i]]==)
{
if (tt[i]==father[root])
work(tt[i],sum-s[root]);
else
work(tt[i],s[tt[i]]);
}
i=pre[i];
}
//------------------------------
i=p[root];
vsmn[]=;
vsmx[]=;
while (i)
{
if (!flag[tt[i]])
{
for (j=;j<=U;j++)
MN[j]=0x37373737;
for (j=;j<=V;j++)
MX[j]=;
U=;V=;
dfs(tt[i],root,,);
for (j=;j<=U;j++)
{
if (MN[j]-d<=v[root])
ans=max(ans,j);
k=MN[j]-d;
if (k<) k=;
if (k<=v[root])
ans=max(ans,j+query(,k-,v[root],)-);
}
for (j=;j<=V;j++)
{
if (MX[j]+d>=v[root])
ans=max(ans,j);
k=MX[j]+d;
if (k>) k=;
if (k>=v[root])
ans=max(ans,j+query(,v[root]-,k,)-);
} for (j=;j<=U;j++)
change(,MN[j]-,MN[j],j,);
for (j=;j<=V;j++)
change(,MX[j]-,MX[j],j,);
}
i=pre[i];
}
flag[root]=;
}
int main()
{
build(,,);
for (i=;i<=;i++)
MN[i]=0x37373737;
int test,ii=;
scanf("%d",&test);
while (test)
{
test--;
ii++;
dp=;memset(p,,sizeof(p));
scanf("%d%d",&n,&d);
for (i=;i<=n;i++)
scanf("%d",&v[i]);
for (i=;i<=n-;i++)
{
scanf("%d%d",&a,&b);
link(a,b);
link(b,a);
}
ans=;
work(,n);
printf("Case #%d: %d\n",ii,ans);
}
}

UVALive 7148 LRIP【树分治+线段树】的更多相关文章

  1. 【BZOJ4372】烁烁的游戏 动态树分治+线段树

    [BZOJ4372]烁烁的游戏 Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠.题意:给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠.烁烁他每次会跳到一个节点u,把周围与他距 ...

  2. LOJ#6463 AK YOI 树分治+线段树合并

    传送门 既然是树上路径统计问题,不难想到要使用树分治,这里以点分治为例 由点分治的性质,每层只需要考虑经过重心的路径 因为需要维护路径长度在一定范围内的最大权值和,所以要用一个数据结构维护一下到根节点 ...

  3. 【BZOJ3730】震波 动态树分治+线段树

    [BZOJ3730]震波 Description 在一片土地上有N个城市,通过N-1条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为1,其中第i个城市的价值为value[i].不幸的是,这片土 ...

  4. 7.18 NOI模拟赛 因懒无名 线段树分治 线段树维护直径

    LINK:因懒无名 20分显然有\(n\cdot q\)的暴力. 还有20分 每次只询问一种颜色的直径不过带修改. 容易想到利用线段树维护直径就可以解决了. 当然也可以进行线段树分治 每种颜色存一下直 ...

  5. bzoj3730 [震波][动态树分治+线段树+LCA]

    震波 Time Limit: 15 Sec  Memory Limit: 256 MBSubmit: 1573  Solved: 358[Submit][Status][Discuss] Descri ...

  6. BZOJ4317Atm的树&BZOJ2051A Problem For Fun&BZOJ2117[2010国家集训队]Crash的旅游计划——二分答案+动态点分治(点分树套线段树/点分树+vector)

    题目描述 Atm有一段时间在虐qtree的题目,于是,他满脑子都是tree,tree,tree…… 于是,一天晚上他梦到自己被关在了一个有根树中,每条路径都有边权,一个神秘的声音告诉他,每个点到其他的 ...

  7. 【loj6145】「2017 山东三轮集训 Day7」Easy 动态点分治+线段树

    题目描述 给你一棵 $n$ 个点的树,边有边权.$m$ 次询问,每次给出 $l$ .$r$ .$x$ ,求 $\text{Min}_{i=l}^r\text{dis}(i,x)$ . $n,m\le ...

  8. 【bzoj4372】烁烁的游戏 动态点分治+线段树

    题目描述 给一颗n个节点的树,边权均为1,初始点权均为0,m次操作:Q x:询问x的点权.M x d w:将树上与节点x距离不超过d的节点的点权均加上w. 输入 第一行两个正整数:n,m接下来的n-1 ...

  9. 【bzoj3730】震波 动态点分治+线段树

    题目描述 在一片土地上有N个城市,通过N-1条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为1,其中第i个城市的价值为value[i].不幸的是,这片土地常常发生地震,并且随着时代的发展,城市 ...

随机推荐

  1. Python之 continue继续循环和多重循环

    Python之 continue继续循环 在循环过程中,可以用break退出当前循环,还可以用continue跳过后续循环代码,继续下一次循环. 假设我们已经写好了利用for循环计算平均分的代码: L ...

  2. eclipse Maven -->web project

    http://blog.chinaunix.net/uid-26959955-id-3248053.html http://blog.csdn.net/wilsonke/article/details ...

  3. 【Android测试】【第十节】MonkeyRunner—— 录制回放

    ◆版权声明:本文出自胖喵~的博客,转载必须注明出处. 转载请注明出处:http://www.cnblogs.com/by-dream/p/4861693.html 前言 在实际项目进行过程中,频繁的需 ...

  4. github添加SSH Key

    1.背景介绍 缘由:在github上下载goagent时出现错误. 在终端使用命令:git clone git@github.com:phuslu/goagent.git 会报权限错误如下: Clon ...

  5. NSPredicate 根据谓语动词 进行 模糊查询

    /** *  模糊查询 */ NSString *filterString = textField.text; NSPredicate *predicate = [NSPredicate predic ...

  6. JMeter学习-012-JMeter 配置元件之-HTTP Cookie管理器-实现 Cookie 登录

    前文我们讲过了若何获取登录后的 Cookie 信息,不知如何获取登录 Cookie 的朋友,敬请参阅我之前写的博文:Fiddler-005-获取 Cookie 信息.参阅上篇文章,获取到 Cookie ...

  7. 【转】Android M新控件之AppBarLayout,NavigationView,CoordinatorLayout,CollapsingToolbarLayout的使用

    Android M新控件之AppBarLayout,NavigationView,CoordinatorLayout,CollapsingToolbarLayout的使用 分类: Android UI ...

  8. C语言中malloc()和calloc()c函数用法

    C语言中malloc()和calloc()c函数用法   函数malloc()和calloc()都可以用来动态分配内存空间,但两者稍有区别. malloc()函数有一个参数,即要分配的内存空间的大小: ...

  9. H3C S3600-28TP-SI配置命令

    模式分类为:<H3C> 用户视图       [H3C] 系统视图    [H3C-Ethernet1/0/1] 以太网端口视图 [H3C-vlan10] VLAN视图   [H3C-Vl ...

  10. SLAM学习笔记(3)相关概念

    SIFT,即尺度不变特征变换(Scale-invariant feature transform,SIFT),是用于图像处理领域的一种描述子.这种描述具有尺度不变性,可在图像中检测出关键点,是一种局部 ...