[乱搞]hdu 6406 Taotao picks apples 笛卡尔树+倍增
When Taotao picks apples, Taotao scans these apples from the first one to the last one. If the current apple is the first apple, or it is strictly higher than the previously picked one, then Taotao will pick this apple; otherwise, he will not pick.
Given the heights of these apples h1,h2,⋯,hn, you are required to answer some independent queries. Each query is two integers p,q, which asks the number of apples Taotao would pick, if the height of the p-th apple were q (instead of hp). Can you answer all these queries?
Each test case begins with a line of two integers n,m (1≤n,m≤105), denoting the number of apples and the number of queries. It is then followed by a single line of n integers h1,h2,⋯,hn (1≤hi≤109), denoting the heights of the apples. The next m lines give the queries. Each of these m lines contains two integers p (1≤p≤n) and q (1≤q≤109), as described in the problem statement.
5 3
1 2 3 4 4
1 5
5 5
2 3
5
3
For the first query, the heights of the apples were 5, 2, 3, 4, 4, so Taotao would only pick the first apple.
For the second query, the heights of the apples were 1, 2, 3, 4, 5, so Taotao would pick all these five apples.
For the third query, the heights of the apples were 1, 3, 3, 4, 4, so Taotao would pick the first, the second and the fourth apples.
无论用哪一种算法,对于这道题目,最重要的就是高效的完成从一个苹果找到下一个苹果,那么笛卡尔树用来处理这样的操作是否足够呢?(笛卡尔树的解释可以参考这篇博客)
把一个数组按笛卡尔树的方式组织,根节点是整个数组最大的节点,然后以这个节点把数组划分为两部分,左右子结点分别又是左右部分的最大节点,以此类推。这样处理以后,从根节点一直向左走的路径,就是从第一个苹果出发将得到的递增序列。因此,我们只用考虑每次询问以后,这条路径会发生怎样的变化,而不必考虑整棵树的变化(如果树在不断更新就得考虑,只是需要进行非常细致的讨论)。询问大致可以分为4类:路径上的点变大/变小,非路径上的点变大/变小。
情况一:路径上的点变大
因为是增大,所以不会有新的点出现,只会覆盖已有的点。只需要从当前节点出发找到第一个比q大的节点就可以了。
情况二:路径上的点变小
假设这个点是a,路径上的下一个点是b,那么当a变为a1的时候,在a,b之间本来被a覆盖的数字,就需要重新考虑。也就是从这个点右边的第一个数开始寻找,一直向上直到第一个比a1大的数。
但是当a继续变小的时候,它将被路径上的前一个点c覆盖,而下一个点就应该是到第一个比c大的数
情况三:非路径上的点变大
考虑这个点在路径上的最近祖先。只有当它的变得比它的祖先还大,才会影响路径。而且它只会覆盖这个祖先的祖先。
情况四:非路径上的点变小
对路径无影响
根据上面四个情况的分析,看似有很多边的变化,但最终只需要考虑有多少点要添加到路径上,其余的怎么变化不必考虑。
接下来就是终于到了算法的核心了,使用笛卡尔树只是将数组整理了成了一个比较好的结构,还需要一个高效寻找节点的方法。因为笛卡尔树的性质,向上走永远是在变大,每一个向上的过程都是单调的,可以用倍增的思想去寻找,每次尝试走1步,2步,4步,8步,直到遇到大的节点。
算法的复杂度不会算了,当时是这样分析的:建笛卡尔树为O(n),每次查询找下一个节点的复杂度在O(log(n))以内,如果建出来的笛卡尔树比较平衡的话,甚至可能接近O(log(log(n))),但因为数据随机以及可能有专门卡的数据,一次询问复杂度大致为O(log(n)),总共m次询问,最坏情况下也为O(mlogn)。
#include<stdio.h>
#include<memory.h>
#define N_max 100005
int n, m, ipt[N_max];
//#define debug there_is_no_bug_at_all_and_you_should_not_be_able_to_see_these_words_:( /*手动简易栈*/
int slen, stackhelp[N_max];
inline void push(int x) { stackhelp[slen++] = x; }
inline void pop() { slen--; }
inline int top() { return slen> ? stackhelp[slen - ] : -; }
inline void clear() { slen = ; } //笛卡尔树
typedef struct {
int l, r, p, v;
void newnode() { l = -; r = -; p = -; v = ; }
}node;
node tree[N_max];
int g[N_max][];
int on[N_max]; int lay[N_max];
void treeinit() {
clear();
memset(g, , sizeof g);
memset(lay, , sizeof lay);
memset(on, , sizeof on);
tree[].newnode();
tree[].v = 0x3f3f3f3f;
push();
} void add(int id, int v) {
tree[id].newnode();//准备好节点
while (slen > && tree[top()].v < v)pop();
int rt = top();
tree[id].v = v;
if (tree[rt].r > ) {/*子树挂到id左边*/
tree[tree[rt].r].p = id;
tree[id].l = tree[rt].r;
}
//id挂到根右边
tree[rt].r = id;
tree[id].p = rt;
push(id);//入栈
} int fb;
void dfs1(int cur){/*求倍增数组,顺带标记一下节点的层数*/
if (cur == -)return;
lay[cur] = (cur==?:(lay[tree[cur].p] + ));
g[cur][] = tree[cur].p;
for (int i = ;; ++i) {
fb = g[cur][i - ]; if (fb == )break;
g[cur][i] = g[fb][i - ];
}
dfs1(tree[cur].l);
dfs1(tree[cur].r);
} int key;
int find(int cur) {//利用倍增的思想查找第一个比key大的节点
if (cur == -)return -;
if (tree[cur].v>key)return cur; int fb = g[cur][];
if (tree[fb].v>key)return fb;
int jmp = fb;
for (int i = ;; ++i) {//向上试探
fb = g[cur][i]; if (fb == )break;
if (tree[fb].v>key)break;
jmp = fb;
}
return find(jmp);//可能没有一次找到,继续向上试探
} void color(int cur){//把一颗子树完全涂色为根节点
if (cur == -)return;
on[cur] = on[tree[cur].p];
color(tree[cur].l);
color(tree[cur].r);
} void geton() {//从根节点出发,把路径标记出来,并把路径上节点的右子树涂色为该节点
int cur = tree[].r;
do { on[cur] = cur;color(tree[cur].r); cur = tree[cur].l; } while (cur != -);
} int sol(int p,int q){
int x,y;
if(on[p]==p){//在路径上的操作
if(tree[p].v<q){//变大,考虑将被覆盖的点的个数
key = q; x = find(p);
return lay[] - lay[p] + + lay[x];
}
else{//变小
if(tree[p].r==-){
//没有右子树,则一定不会出现新的点,只考虑当前点是否会被前一个点覆盖
return (lay[]- ((tree[p].l==-)?:(q <= tree[tree[p].l].v)));
}
//考虑将从右子树新增几个点到路径上
key = q;
if(tree[p].l!=-&&q<=tree[tree[p].l].v)
//左边有点并且更新后比前一个点更小
key = tree[tree[p].l].v;
y = find(p + );
return lay[] - (q <= tree[tree[p].l].v) + lay[y] - lay[p];
}
}
else{//操作非路径上的点
if (tree[p].v<q){//变大
key = q; x = find(p);
if(on[x]==x)//变大到足够影响原序列,考虑将被覆盖的点的个数
return (lay[] - lay[on[p]] + lay[x]+(x!=on[p])+(q>tree[on[p]].v));
}
return (lay[]);//不足以影响,无变化
}
return ;//正常情况下不该出现的返回值(误
} int main()
{
int kase, p, q;
scanf("%d", &kase);
while (kase--) {
scanf("%d %d", &n, &m);
treeinit();
for (int i = ; i <= n; ++i){
scanf("%d", ipt + i);
add(i, ipt[i]);
}
lay[] = ;
dfs1();
geton();
for (int i = ; i<m; ++i) {
scanf("%d %d", &p, &q);
printf("%d\n",sol(p,q));
}
}
return ;
}
[乱搞]hdu 6406 Taotao picks apples 笛卡尔树+倍增的更多相关文章
- hdu 6406 Taotao Picks Apples (线段树)
Problem Description There is an apple tree in front of Taotao's house. When autumn comes, n apples o ...
- hdu 6406 Taotao Picks Apples 线段树 单点更新
Taotao Picks Apples Time Limit: 2000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Ot ...
- hdu 6406 Taotao Picks Apples (2018 Multi-University Training Contest 8 1010)(二分,前缀和)
链接:http://acm.hdu.edu.cn/showproblem.php?pid=6406 思路: 暴力,预处理三个前缀和:[1,n]桃子会被摘掉,1到当前点的最大值,1到当前点被摘掉的桃子的 ...
- HDU 6406 Taotao Picks Apples & FJUT3592 做完其他题后才能做的题(线段树)题解
题意(FJUT翻译HDU): 钱陶陶家门前有一棵苹果树. 秋天来了,树上的n个苹果成熟了,淘淘会去采摘这些苹果. 到园子里摘苹果时,淘淘将这些苹果从第一个苹果扫到最后一个. 如果当前的苹果是第一个苹果 ...
- HDU 6406 Taotao Picks Apples 线段树维护
题意:给个T,T组数据: 每组给个n,m:n个数,m个操作: (对序列的操作是,一开始假设你手上东西是-INF,到i=1时拿起1,之后遍历,遇到比手头上的数量大的数时替换(拿到手的算拿走),问最后拿走 ...
- HDU - 6406 Taotao Picks Apples (RMQ+dp+二分)
题意:N个高度为hi的果子,摘果子的个数是从位置1开始从左到右的严格递增子序列的个数.有M次操作,每次操作对初始序列修改位置p的果子高度为q.每次操作后输出修改后能摘到得数目. 分析:将序列分为左.右 ...
- hdu6406 Taotao Picks Apples(线段树)
Taotao Picks Apples 题目传送门 解题思路 建立一颗线段树,维护当前区间内的最大值maxx和可摘取的苹果数num.最大值很容易维护,主要是可摘取的苹果数怎么合并.合并左右孩子时,左孩 ...
- 【杂题总汇】HDU-6406 Taotao Picks Apples
[HDU 6406]Taotao Picks Apples 多校赛的时候多写了一行代码就WA了……找了正解对拍,在比赛结束后17分钟AC了
- HDU - 6305 RMQ Similar Sequence(笛卡尔树)
http://acm.hdu.edu.cn/showproblem.php?pid=6305 题目 对于A,B两个序列,任意的l,r,如果RMQ(A,l,r)=RMQ(B,l,r),B序列里的数为[0 ...
随机推荐
- 20155301 2016-2017-2 《Java程序设计》第2周学习总结
20155301 2016-2017-2 <Java程序设计>第2周学习总结 教材学习内容总结 教材的第三章内容主要分为两大方面: 一.程序中的数据类型,变量和运算符,主要讲述了各种类型的 ...
- 北京Uber优步司机奖励政策(4月12日)
滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...
- sqlserver安装遇到的问题——1
SQL Server安装过无数次,今天第一次遇到这样的问题 一.问题消息复制出来是这样的 TITLE: Microsoft SQL Server 2008 R2 安装程序--------------- ...
- 【JUC源码解析】ThreadPoolExecutor
简介 ThreadPoolExecutor,线程池的基石. 概述 线程池,除了用HashSet承载一组线程做任务以外,还用BlockingQueue承载一组任务.corePoolSize和maximu ...
- 探寻ASP.NET MVC鲜为人知的奥秘(1):对LESS的支持
在ASP.NET MVC3中(从那时开始),我们拥有了对js和css等文件的捆绑(Bundling)和压缩(Minification)的能力,这是ASP.NET性能优化工作的一部分. 想一下很久以前, ...
- python爬虫:爬取慕课网视频
前段时间安装了一个慕课网app,发现不用注册就可以在线看其中的视频,就有了想爬取其中的视频,用来在电脑上学习.决定花两天时间用学了一段时间的python做一做.(我的新书<Python爬虫开发与 ...
- oracle数据库应用性能优化经验(培训讲义)
这是我给公司同事做的内部培训ppt的讲义,给大家分享一下.这是培训大纲,ppt在找地方上传,等找到了会把链接发在这里 . 暂时放在csdn上,赚点下载积分:https://download.csdn. ...
- Unity中StopCoroutine不起作用怎么办
1,只有StartCoroutine使用一个字符串方法名时才能用StopCoroutine(string CoroutineName)停用. 2, public Coroutine coroutine ...
- Siki_Unity_2-4_UGUI_Unity5.1 UI 案例学习
Unity 2-4 UGUI Unity5.1 UI 案例学习 任务1-1:UGUI简介 什么是GUI: 游戏的开始菜单 RPG游戏的菜单栏.侧边栏和功能栏(比如背包系统.任务列表等) 设计用来控制移 ...
- 高可用Kubernetes集群-1. 集群环境
参考文档: 部署kubernetes集群1:https://github.com/opsnull/follow-me-install-kubernetes-cluster 部署kubernetes集群 ...