[乱搞]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 ...
随机推荐
- 20155234 实验二 Java面向对象程序设计
实验二 Java面向对象程序设计 实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S.O.L.I.D原则 了解设计模式 实验步骤 (一)单元测试 ...
- java 万能转换器 输入SQL 直接得到ArrayList
//java万能List转换器 public static <T> ArrayList<T> ToList(Class<T> clazz,String sql) t ...
- oracle基础命令
oracle使用步骤: 一.oracle安装 两个文件解压到同一文件夹,doc为说明/使用文档 二.oracle启动: 1.启动oracle:启动监听和自定义库 2.启动cmd->sqlplus ...
- 人脸识别引擎SeetaFaceEngine简介及在windows7 vs2013下的编译
SeetaFaceEngine是开源的C++人脸识别引擎,无需第三方库,它是由中科院计算所山世光老师团队研发.它的License是BSD-2. SeetaFaceEngine库包括三个模块:人脸检测( ...
- Codeception (安装)
来源:http://codeception.com/install 注意:打开Codeception的官网需要FQ 1. 下载 下载地址:http://codeception.com/thanks 或 ...
- protobuf工程的编译以及使用
一. 获取Protocol Buffer 1.1 获得源码 Github:ProtocolBuffer源码 Git clone之:git clone https://github.com/google ...
- bilibili携手WeTest,保障视频类应用优质适配体验
WeTest 导读 中国移动视频用户规模越来越大,各类移动视频APP也百家争鸣, B站作为国内知名的年轻人文化社区,bilibili在推出移动端时,除了坚持自身的独特定位以外,对其APP的质量也十分重 ...
- javaweb(十九)——JSP标签
一.JSP标签介绍 JSP标签也称之为Jsp Action(JSP动作)元素,它用于在Jsp页面中提供业务逻辑功能,避免在JSP页面中直接编写java代码,造成jsp页面难以维护. 二.JSP常用标签 ...
- Openwrt能用的花生壳客户端
http://files.cnblogs.com/mazhiyong/phddns.zip 使用教程可参考官方文档 http://service.oray.com/question/116.html
- leetcode个人题解——#40 Combination Sum2
思路:解法和39题类似,改动了两处: 1.因为题目要求每个元素只能出现一次(不代表每个数只能有一个,因为数据中会有重复的数字),所以代码中21行搜索时下一次循环的位置+1: 2.将临时存放答案的vec ...