[BZOJ4553][HEOI2016]序列 CDQ分治

4553: [Tjoi2016&Heoi2016]序列

Time Limit: 20 Sec  Memory Limit: 128 MB

Description

佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他。玩具上有一个数列,数列中某些项的值

可能会变化,但同一个时刻最多只有一个值发生变化。现在佳媛姐姐已经研究出了所有变化的可能性,她想请教你

,能否选出一个子序列,使得在任意一种变化中,这个子序列都是不降的?请你告诉她这个子序列的最长长度即可

。注意:每种变化最多只有一个值发生变化。在样例输入1中,所有的变化是:

1 2 3

2 2 3

1 3 3

1 1 31 2 4

选择子序列为原序列,即在任意一种变化中均为不降子序列在样例输入2中,所有的变化是:3 3 33 2 3选择子序列

为第一个元素和第三个元素,或者第二个元素和第三个元素,均可满足要求

Input

输入的第一行有两个正整数n, m,分别表示序列的长度和变化的个数。接下来一行有n个数,表示这个数列原始的

状态。接下来m行,每行有2个数x, y,表示数列的第x项可以变化成y这个值。1 <= x <= n。所有数字均为正整数

,且小于等于100,000

Output

输出一个整数,表示对应的答案

Sample Input

3 4

1 2 3

1 2

2 3

2 1

3 4

Sample Output

3

题解:

我们来分析一下这道题让我们干什么:

我们知道了一个序列,其中每一个元素都可能变化,

我们设他的原始值为a[i],最大值为maxv[i],最小值为minv[i],

再设f[i]为以i为结尾的最长符合要求子序列,显然这可以用一个dp来解决:对于f[i],有

  f[i]=max{f[j]}+1

而对j的要求,由于同时只有一个元素发生变化,我们就要求满足

  j<i&&maxv[j]<=a[i]&&a[j]<=minv[i]

我们发现,这好像长得“很像”一个三维偏序问题。

如果我们用树套树来解决的话,也不是不可以(详见勇士的战斗记录:BZOJ4553: [Tjoi2016&Heoi2016]序列 树套树优化DP

但是为什么我们不用更简单的做法来解决呢?

显然,这个东西是可以用cdq分治来解决的

我们对于区间[l,r],如果这个元素i在mi前面,我们就用(maxv[i],a[i])作为他的权值;否则,就用(a[i],minv[i])来作为他的权值。

这样,就可以实现上面的想法了:用前面来更新后面。这也是本题的关键。

想到了这一点,代码实现就很简单了。代码见下:

 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 const int N=300000;
 7 int n,m,bit[N+100],f[N+100];
 8 struct num{int val,maxv,minv;}x[N+100];
 9 struct cdq{int x,y,id;}a[N+100];
10 inline int lowbit(int a){return a&(-a);}
11 inline bool mt(const cdq &a,const cdq &b)
12 {return (a.x==b.x)?a.id<b.id:a.x<b.x;}
13 inline void add(int i,int val)
14 {
15     while(i<=N)
16     {
17         bit[i]=(val==0)?0:max(bit[i],val);
18         i+=lowbit(i);
19     }
20 }
21 inline int sum(int i)
22 {
23     int ret=0;
24     while(i)
25         ret=max(ret,bit[i]),i-=lowbit(i);
26     return ret;
27 }
28 void cdq(int l,int r)
29 {
30     if(l==r){f[l]=max(f[l],1);return;}
31     int mi=(l+r)>>1;
32     cdq(l,mi);
33     for(int i=l;i<=r;i++)
34     {
35         if(i<=mi)a[i].x=x[i].val,a[i].y=x[i].maxv;
36         else a[i].x=x[i].minv,a[i].y=x[i].val;
37         a[i].id=i;
38     }
39     sort(a+l,a+r+1,mt);
40     for(int i=l;i<=r;i++)
41     {
42         if(a[i].id<=mi)add(a[i].y,f[a[i].id]);
43         else f[a[i].id]=max(sum(a[i].y)+1,f[a[i].id]);
44     }
45     for(int i=l;i<=r;i++)add(a[i].y,0);
46     cdq(mi+1,r);
47 }
48 int main()
49 {
50     scanf("%d%d",&n,&m);int u,v,ans=0;
51     for(int i=1;i<=n;i++)
52         scanf("%d",&x[i].val),x[i].minv=x[i].maxv=x[i].val;
53     while(m--)
54     {
55         scanf("%d%d",&u,&v);
56         x[u].maxv=max(x[u].maxv,v);
57         x[u].minv=min(x[u].minv,v);
58     }
59     cdq(1,n);
60     for(int i=1;i<=n;i++)ans=max(ans,f[i]);
61     printf("%d\n",ans);
62 }
时间: 2024-11-06 18:20:36

[BZOJ4553][HEOI2016]序列 CDQ分治的相关文章

【BZOJ4553】[Tjoi2016&amp;Heoi2016]序列 cdq分治+树状数组

[BZOJ4553][Tjoi2016&Heoi2016]序列 Description 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他.玩具上有一个数列,数列中某些项的值可能会变化,但同一个时刻最多只有一个值发生变化.现在佳媛姐姐已经研究出了所有变化的可能性,她想请教你,能否选出一个子序列,使得在任意一种变化中,这个子序列都是不降的?请你告诉她这个子序列的最长长度即可.注意:每种变化最多只有一个值发生变化.在样例输入1中,所有的变化是: 1 2 3 2 2 3 1 3 3 1

[HEOI2016/TJOI2016]序列 CDQ分治

---题面--- 题解: 首先我们观察一下,如果一个点对(j, i), 要符合题中要求要满足哪些条件? 首先我们设 j < i 那么有: j < i max[j] < v[i] v[j] < min[i] (注意下面两个式子都是用的v[i],v[j],,,而不是i , j...之前因为这个问题纠结了很久,其实我也不知道我在纠结什么...) 这三个式子是不是很眼熟? 如果式子变成: j < i max[j] < max[i] min[j] < min[i] 是不是就

BZOJ 4553 Tjoi2016&amp;Heoi2016 序列

Tjoi2016&Heoi2016序列 Description 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他.玩具上有一个数列,数列中某些项的值 可能会变化,但同一个时刻最多只有一个值发生变化.现在佳媛姐姐已经研究出了所有变化的可能性,她想请教你 ,能否选出一个子序列,使得在任意一种变化中,这个子序列都是不降的?请你告诉她这个子序列的最长长度即可 .注意:每种变化最多只有一个值发生变化.在样例输入1中,所有的变化是: 1 2 3 2 2 3 1 3 3 1 1 31 2 4

BZOJ 2225 [Spoj 2371]Another Longest Increasing(CDQ分治)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2225 [题目大意] 给定N个数对(xi,yi),求最长上升子序列的长度. 上升序列定义为{(xi,yi)}满足对i<j有xi<xj且yi<yj. [题解] CDQ分治,将每个区间按照a排序,用区间左边的数据来更新右边的最长上升序列, 为排除a相等但是b上升情况的误统计,在排序时加入下标作为第二关键字, 使得a相等的情况下标小的后更新. [代码] #include <cs

BZOJ 2726: [SDOI2012]任务安排( dp + cdq分治 )

考虑每批任务对后面任务都有贡献, dp(i) = min( dp(j) + F(i) * (T(i) - T(j) + S) ) (i < j <= N)  F, T均为后缀和. 与j有关的量只有t = dp(j) - F(i) * T(j) , 我们要最小化它. dp(j)->y, T(j)->x, 那么y = F(i) * x + t, 就是给一些点和一个斜率...然后最小化截距, 显然维护下凸包就可以了. 然后因为无比坑爹的出题人....时间可以为负数, 所以要用平衡树维护(

CDQ分治与整体二分总结

Cdq分治和整体二分是两个很奇妙的东西.他们都是通过离线的思想来进行优化,从而更快的求出解. 整体二分通俗的讲就是二分答案,但是它了不起的地方是一下子把所有的答案都二分出来了,从而可以一下子得出所有查询. CDQ分治通俗的讲就是二分查询.通常的做法是把所有的查询分成两半,然后通过递归先计算出左边一半的所有的查询,然后通过这些已知的左半边的值来更新右半边的值.这里,最最重要的思想是通过左半边来更新右半边.更具体一点,就是用左半边的修改来更新右半边的查询. 重要的事情说话三遍: CDQ分治就是通过左

HDU5730 FFT+CDQ分治

题意:dp[n] = ∑ ( dp[n-i]*a[i] )+a[n], ( 1 <= i < n) cdq分治. 计算出dp[l ~ mid]后,dp[l ~ mid]与a[1 ~ r-l]做卷积运算. 1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 4e5+5; 4 const int mod = 313; 5 const double pi = acos(-1.0); 6 struct comp

学习笔记: cdq分治

今年的课程有很大一部分内容是cdq分治及其扩展(也就是二进制分组),拜读后觉得还是蛮有用的,这里小小地总结一下.(话说自己草稿箱里还有好多学习笔记的半成品呢,真是弱爆了.顺便感谢下fy与wxl向我介绍了那么好的东西) 推荐论文: 1 <从<Cash>谈一类分治算法的应用> 陈丹琦 2 <浅谈数据结构题的几个非经典解法>  许昊然 Q: cdq分治和普通的分治有什么区别? A: 在我们平常使用的分治中,每一个子问题只解决它本身(可以说是封闭的).而在cdq分治中,对于划分

bzoj 3295: [Cqoi2011]动态逆序对(树套树 or CDQ分治)

Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数.以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为每次删除的元素. Output 输出包含m行,依次为删除每个元素之前,逆序对的个数. Sample Input 5 4 1 5 3