UVALive 7148 LRIP【树分治+线段树】

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

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

  代码:

  1 #include<cstdio>
  2 #include<algorithm>
  3 #include<cstring>
  4 #define N 1000010
  5 using namespace std;
  6 int dp,p[N],s[N],pre[N],tt[N],flag[N],father[N],tmproot,n,d,ttt;
  7 int i,v[N],a,b,mn,mx,MN[N],MX[N],U,V,ans;
  8 int l[N],r[N],smn[N],smx[N],vsmn[N],vsmx[N];
  9 void build(int x,int a,int b)
 10 {
 11     int m;
 12     l[x]=a;r[x]=b;
 13     if (x>ttt) ttt=x;
 14     if (b-a>1)
 15     {
 16         m=(a+b)>>1;
 17         build(2*x,a,m);
 18         build(2*x+1,m,b);
 19     }
 20 }
 21 void clean(int x)
 22 {
 23     if (vsmn[x])
 24     {
 25         smn[x]=0;
 26         if (2*x<=ttt)
 27         vsmn[2*x]=1;
 28         if (2*x+1<=ttt)
 29         vsmn[2*x+1]=1;
 30         vsmn[x]=0;
 31     }
 32
 33     if (vsmx[x])
 34     {
 35         smx[x]=0;
 36         if (2*x<=ttt)
 37         vsmx[2*x]=1;
 38         if (2*x+1<=ttt)
 39         vsmx[2*x+1]=1;
 40         vsmx[x]=0;
 41     }
 42 }
 43 void change(int x,int a,int b,int c,int typ)
 44 {
 45     int m;
 46     clean(x);
 47     if ((a<=l[x])&&(r[x]<=b))
 48     {
 49         if (typ==0)
 50         smn[x]=max(smn[x],c);
 51         else
 52         smx[x]=max(smx[x],c);
 53         return;
 54     }
 55     m=(l[x]+r[x])>>1;
 56     if (a<m) change(2*x,a,b,c,typ);
 57     if (m<b) change(2*x+1,a,b,c,typ);
 58     clean(2*x);clean(2*x+1);
 59     if (!typ)
 60     smn[x]=max(smn[2*x],smn[2*x+1]);
 61     else
 62     smx[x]=max(smx[2*x],smx[2*x+1]);
 63 }
 64 int query(int x,int a,int b,int typ)
 65 {
 66     int m,ans=0;
 67     clean(x);
 68     if ((a<=l[x]&&(r[x]<=b)))
 69     {
 70         if (typ==0)
 71         return smn[x];
 72         else
 73         return smx[x];
 74     }
 75     m=(l[x]+r[x])>>1;
 76     if (a<m) ans=max(ans,query(2*x,a,b,typ));
 77     if (m<b) ans=max(ans,query(2*x+1,a,b,typ));
 78     return ans;
 79 }
 80 void link(int x,int y)
 81 {
 82     dp++;pre[dp]=p[x];p[x]=dp;tt[dp]=y;
 83 }
 84 int getroot(int x,int fa,int sum)
 85 {
 86     int i,f=0;
 87     i=p[x];
 88     father[x]=fa;
 89     s[x]=1;
 90     while (i)
 91     {
 92         if ((tt[i]!=fa)&&(!flag[tt[i]]))
 93         {
 94             getroot(tt[i],x,sum);
 95             s[x]=s[x]+s[tt[i]];
 96             if (s[tt[i]]>sum/2) f=1;
 97         }
 98         i=pre[i];
 99     }
100     if (sum-s[x]>sum/2) f=1;
101     if (!f) tmproot=x;
102 }
103 void dfs(int x,int fa,int mn,int mx)
104 {
105     int i;
106     i=p[x];
107     if (v[x]>=v[fa])
108     {
109         if (mn)
110         {
111             mn++;
112             MN[mn]=min(MN[mn],v[x]);
113         }
114         if (v[x]>v[fa])
115         mx=0;
116     }
117     if (v[x]<=v[fa])
118     {
119         if (mx)
120         {
121             mx++;
122             MX[mx]=max(MX[mx],v[x]);
123         }
124         if (v[x]<v[fa])
125         mn=0;
126     }
127     U=max(U,mn);
128     V=max(V,mx);
129     while (i)
130     {
131         if ((tt[i]!=fa)&&(!flag[tt[i]]))
132             dfs(tt[i],x,mn,mx);
133         i=pre[i];
134     }
135 }
136 void work(int x,int sum)
137 {
138     int i,j,k,root;
139     getroot(x,0,sum);
140     root=tmproot;
141     i=p[root];
142     flag[root]=1;
143     while (i)
144     {
145         if (flag[tt[i]]==0)
146         {
147             if (tt[i]==father[root])
148             work(tt[i],sum-s[root]);
149             else
150             work(tt[i],s[tt[i]]);
151         }
152         i=pre[i];
153     }
154 //------------------------------
155     i=p[root];
156     vsmn[1]=1;
157     vsmx[1]=1;
158     while (i)
159     {
160         if (!flag[tt[i]])
161         {
162             for (j=2;j<=U;j++)
163             MN[j]=0x37373737;
164             for (j=2;j<=V;j++)
165             MX[j]=0;
166             U=0;V=0;
167             dfs(tt[i],root,1,1);
168             for (j=2;j<=U;j++)
169             {
170                 if (MN[j]-d<=v[root])
171                 ans=max(ans,j);
172                 k=MN[j]-d;
173                 if (k<1) k=1;
174                 if (k<=v[root])
175                 ans=max(ans,j+query(1,k-1,v[root],1)-1);
176             }
177             for (j=2;j<=V;j++)
178             {
179                 if (MX[j]+d>=v[root])
180                 ans=max(ans,j);
181                 k=MX[j]+d;
182                 if (k>100000) k=100000;
183                 if (k>=v[root])
184                 ans=max(ans,j+query(1,v[root]-1,k,0)-1);
185             }
186
187
188             for (j=2;j<=U;j++)
189             change(1,MN[j]-1,MN[j],j,0);
190             for (j=2;j<=V;j++)
191             change(1,MX[j]-1,MX[j],j,1);
192         }
193         i=pre[i];
194     }
195     flag[root]=0;
196 }
197 int main()
198 {
199     build(1,0,100000);
200     for (i=0;i<=100000;i++)
201     MN[i]=0x37373737;
202     int test,ii=0;
203     scanf("%d",&test);
204     while (test)
205     {
206     test--;
207     ii++;
208     dp=0;memset(p,0,sizeof(p));
209     scanf("%d%d",&n,&d);
210     for (i=1;i<=n;i++)
211     scanf("%d",&v[i]);
212     for (i=1;i<=n-1;i++)
213     {
214         scanf("%d%d",&a,&b);
215         link(a,b);
216         link(b,a);
217     }
218     ans=1;
219     work(1,n);
220     printf("Case #%d: %d\n",ii,ans);
221     }
222 }
时间: 2024-10-22 22:37:06

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

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

[BZOJ4372]烁烁的游戏 Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠.题意:给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠.烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节点各吸引出w只皮皮鼠.皮皮鼠会被烁烁吸引,所以会一直待在节点上不动.烁烁很好奇,在当前时刻,节点u有多少个他的好朋友---皮皮鼠.大意:给一颗n个节点的树,边权均为1,初始点权均为0,m次操作:Q x:询问x的点权.M x d w:将树上与节点x距离不超过d的节点的点权均加上w. In

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

[BZOJ3730]震波 Description 在一片土地上有N个城市,通过N-1条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为1,其中第i个城市的价值为value[i].不幸的是,这片土地常常发生地震,并且随着时代的发展,城市的价值也往往会发生变动.接下来你需要在线处理M次操作:0 x k 表示发生了一次地震,震中城市为x,影响范围为k,所有与x距离不超过k的城市都将受到影响,该次地震造成的经济损失为所有受影响城市的价值和.1 x y 表示第x个城市的价值变成了y.为了体现程序的在

区间树和线段树

注意:区间树和线段树不一样哦,线段树是一种特殊的区间树. 区间树: 区间树是在红黑树基础上进行扩展得到的支持以区间为元素的动态集合的操作,其中每个节点的关键值是区间的左端点.通过建立这种特定的结构,可是使区间的元素的查找和插入都可以在O(lgn)的时间内完成.相比于基础的红黑树数据结构,增加了一个max[x],即以x为根的子树中所有区间的断点的最大值.逻辑结构如下所示: 区间树具有和红黑树一样的性质,并且区间树的基础操作和红黑树的基础操作一样.(具体红黑树的操作,参见http://blog.cs

[BZOJ 1901] Dynamic Rankings 【树状数组套线段树 || 线段树套线段树】

题目链接:BZOJ - 1901 题目分析 树状数组套线段树或线段树套线段树都可以解决这道题. 第一层是区间,第二层是权值. 空间复杂度和时间复杂度均为 O(n log n). 代码 树状数组套线段树 #include <iostream> #include <cstdlib> #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> usin

【vijos】1750 建房子(线段树套线段树+前缀和)

https://vijos.org/p/1750 是不是我想复杂了.... 自己yy了个二维线段树,然后愉快的敲打. 但是wa了两法.......sad 原因是在处理第二维的更新出现了个小问题,sad. void pushup1(int x) { for1(i, 1, mm<<2) mn[x][i]=min(mn[lc][i], mn[rc][i]); } 这里注意是mm*4...我该好好想想了..这是在dbg的时候找出来的问题.sad. 我觉得很奇怪,线段树的底层节点一共就mm个,那么整棵树

【bzoj4785】[Zjoi2017]树状数组 线段树套线段树

题目描述 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道基础的树状数组题.给出一个长度为 n 的数组 A,初始值都为 0,接下来进行 m 次操作,操作有两种: 1 x,表示将 Ax 变成 (Ax + 1) mod 2. 2 l r,表示询问 sigma(Ai) mod 2,L<=i<=r 尽管那个时候的可怜非常的 simple,但是她还是发现这题可以用树状数组做.当时非常young 的她写了如下的算法: 1: function Add(x

主席树/函数式线段树/可持久化线段树

什么是主席树 可持久化数据结构(Persistent data structure)就是利用函数式编程的思想使其支持询问历史版本.同时充分利用它们之间的共同数据来减少时间和空间消耗. 因此可持久化线段树也叫函数式线段树又叫主席树. 可持久化数据结构 在算法执行的过程中,会发现在更新一个动态集合时,需要维护其过去的版本.这样的集合称为是可持久的. 实现持久集合的一种方法时每当该集合被修改时,就将其整个的复制下来,但是这种方法会降低执行速度并占用过多的空间. 考虑一个持久集合S. 如图所示,对集合的

二分索引树与线段树分析

二分索引树是一种树状数组,其全名为Binary Indexed Tree.二分索引树可以用作统计作用,用于计某段连续区间中的总和,并且允许我们动态变更区间中存储的值.二分索引树和线段树非常相似,二者都享有相同的O(log2(n))时间复杂度的更新操作和O(log2(n))时间复杂度的查询操作,区别在于二分索引树更加简洁高效,而线段树则较冗杂低效,原因在于对二分索引树的操作中是使用了计算机中整数存储的特性来进行加速,而线段树中由于使用的是比较操作,因此性能不及二分索引树.那么为什么我们不抛弃线段树

hdu-4819-线段树套线段树

http://acm.hdu.edu.cn/showproblem.php?pid=4819 给出一个N*N的矩阵,每次询问一个m*m的子矩阵里的floor((maxv+minv)/2)并把中间的元素修改为这个值. 线段树套线段树,第一层X表示对行建立的线段树,内层表示对Y也就是列建立的线段树. 分别对X和Y建立相应的函数来完成操作,当更新X树的节点时,再更新完当前X的节点的左右儿子之后,回头对X的这个节点对应的Y树进行更新,相当于X的左右儿子里的Y树来更新X的Y树,能理解这一点就很简单了. 1