[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]

是不是就和三维偏序一模一样了?

但是还是略有区别,不过区别不大,所以我们考虑一个变种的三维偏序。

在之前的板子中我用的是归并排序。

但由于这个题目式子不同。所以归并排序不在合适

首先CDQ可以保证id的相对有序。

而为了比较max[j] 和 v[i]

我们使用sort,分别对左边按max排序,对右边按id排序。

为了比较v[j] < min[i]

我们使用树状数组,

在满足max[j] < v[i]的条件下,

在树状数组的第v[j]位插入大小为ans[j]的数。

查询时用min[i]查询。

树状数组维护最大值(此时不满足区间减法)

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define R register int
  4 #define AC 100100
  5 #define getchar() *o++
  6 #define lowbit(x) (x & (-x))
  7 #define D printf("line in %d\n", __LINE__);
  8 char READ[5000100], *o = READ;
  9 /*变种三维偏序,
 10 总的来说就是要保证这三个式子。
 11 i < j
 12 max[j] < v[i]
 13 v[j] < min[i]
 14 可以看出的三维偏序是非常类似的,
 15 所以也可以用CDQ + 归并 + 树状数组实现。
 16 CDQ维护下标,也就是第一个式子
 17 归并维护第二个式子,
 18 树状数组用于在归并的条件下查询满足第3个式子的贡献。
 19 本质上是类似DP的东西?
 20 p[i].ans 表示以i为结尾的子序列的最长长度
 21 */
 22 /*初始状态下id相对有序,归并维护maxj的顺序
 23 但是这个好像和原来的不一样???
 24 原来的是维护maxj < maxi,两边都是一样的属性,
 25 所以归并完就排好了,因为它的要求对于左边和右边而言是一样的,
 26 都是要按maxj排序,这样的话归并一遍,等到回去的时候这个小块就是按maxj排好序的。
 27 就符合上面的要求。
 28 但是这个maxj < i,两边是不同的属性,所以不太适用。。。
 29 因为这样的话要求小块处理完后左边是按照maxnj排序的,而右边却是按照i排序的。
 30 因此这就不便于处理,也就是根本不能排。
 31 而且这里左边对右边的贡献是有顺序限制的,因此小块处理也要分开
 32 还是要用sort啊......*/
 33 int n, m, ans, maxn;
 34 int power[AC], tmp[AC], c[AC];
 35 struct node{
 36     int id, max, min, ans, v;
 37 }p[AC];
 38
 39 inline int read()
 40 {
 41     int x = 0; char c = getchar();
 42     while(c > ‘9‘ || c < ‘0‘) c = getchar();
 43     while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = getchar();
 44     return x;
 45 }
 46
 47 inline void upmin(int &a, int b)
 48 {
 49     if(a > b) a = b;
 50 }
 51
 52 inline void upmax(int &a, int b)
 53 {
 54     if(b > a) a = b;
 55 }
 56
 57 inline bool cmp1(node a, node b)//按max排序
 58 {
 59     return a.max < b.max;
 60 }
 61
 62 inline bool cmp2(node a, node b)//v排序
 63 {
 64     return a.v < b.v;
 65 }
 66 //按id排序,因为右边块的调用是在处理完左边和左边对右边的贡献后的,
 67 inline bool cmp3(node a, node b)//而这时右边已经不在是id有序的状态了,(因为按v排了序),但是又要求id有序,所以还要还原一次
 68 {
 69     return a.id < b.id;
 70 }
 71
 72 //min由树状数组负责
 73 void pre()
 74 {
 75     int a, b;
 76     n = read(), m = read();
 77     for(R i = 1; i <= n; i++)
 78     {
 79         p[i].id = i;
 80         p[i].v = p[i].max = p[i].min = read();
 81     }
 82     for(R i = 1; i <= m; i++)
 83     {
 84         a = read(), b = read();
 85         upmax(p[a].max, b);
 86         upmax(maxn, b);
 87         upmin(p[a].min, b);
 88     }
 89 }
 90
 91 inline void add(int x, int w)//维护前缀最大值的树状数组(不符合区间减法)
 92 {
 93     for(R i = x; i <= maxn; i += lowbit(i)) upmax(c[i], w);//维护最大值
 94 }//因为维护的格子是以v[i]为基础的,存的东西是ans[i],所以上界自然是max(v[i])
 95
 96 inline int sum(int x)//查询前缀最大值
 97 {
 98     int ans = 0;
 99     for(R i = x; i ; i -= lowbit(i)) upmax(ans, c[i]);
100     return ans;
101 }
102
103 inline void clear(int x)
104 {
105     for(R i = x; i <= maxn; i += lowbit(i)) c[i] = 0;
106 }
107
108 void CDQ(int l, int r)
109 {
110     if(l < r)
111     {
112         int mid = (l + r) >> 1;
113         CDQ(l, mid);
114         sort(p + l, p + mid + 1, cmp1);//按max排序
115         sort(p + mid + 1, p + r + 1, cmp2);//按v排序
116         int i = l, j = mid + 1;
117         while(j <= r)//因为要靠这个代替内层循环,所以这个要用||,但是又不能直接用||,因为会导致死循环(i一直不符合条件)
118         {//j一直往后走,因此直接判断j,相当于外层只是控制j的
119             while(p[i].max <= p[j].v && i <= mid) add(p[i].v, p[i].ans), ++i;
120             upmax(p[j].ans, sum(p[j].min) + 1), ++j;//还要加上自己,因为i往后移动了,所以这个时候再判断大小就不对了,所以干脆强制转移
121         } //让外层循环代替内层循环
122         for(R k = l; k <= i; k++) clear(p[k].v);//error!!!放进去了多少就拿出来多少,不然就浪费了
123         sort(p + mid + 1, p + r + 1, cmp3);//还原id
124         CDQ(mid + 1, r);//因为这个要统计最长长度,是有先后顺序的,陌上花开是在统计个数,是可以允许没有顺序的
125     }
126     else upmax(p[l].ans, 1);//最少也是1了
127 }
128
129 void work()
130 {
131     for(R i = 1; i <= n; i++) upmax(ans, p[i].ans);
132     printf("%d\n", ans);
133 //    printf("time used... %lf\n", (double)clock()/CLOCKS_PER_SEC);
134 }
135
136 int main()
137 {
138 //    freopen("in.in", "r", stdin);
139     fread(READ, 1, 5000000, stdin);
140     pre();
141     CDQ(1, n);
142     work();
143 //    fclose(stdin);
144     return 0;
145 }

原文地址:https://www.cnblogs.com/ww3113306/p/9249812.html

时间: 2024-10-30 00:24:06

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

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

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

[BZOJ4553][HEOI2016]序列 CDQ分治

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

【[HEOI2016/TJOI2016]序列】

压行真漂亮 首先这肯定是一个\(dp\)了 设\(dp_i\)表示\(i\)结尾的最长不下降子序列的长度 显然我们要找一个\(j\)来转移 也就是\(dp_i=max(dp_j+1)\) 那么什么样的\(j\)满足条件呢 首先得是\(j<i\) 我们还注意到一个条件就是这个序列里最多也只有一个位置会发生变化 可能是\(i\)这个位置发生变化,那么显然需要满足对于任意的\(a_i\)都需要满足大于等于\(val_j\) 于是就有\(val_j<=min_i\) 自然也有可能是前面的\(j\)发生

「luogu4093」[HEOI2016/TJOI2016]序列

写出dp方程,可以发现转移要满足一个三维偏序,那么可以处理三维偏序的方法优化. CDQ分治: cdq分治和树状数组是好伙伴~ 注意分治的顺序,要保证先求解出所有前驱状态. 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=100010; 4 int n,m,dp[N],maxn; 5 struct Node{ 6 int a,maxv,minv,id; 7 }node[N]; 8 int cmpa(const Node

# [HEOI2016/TJOI2016]序列

题目链接 戳这 Solution 首先考虑最暴力的dp 我们设: \(f[i]\)表示选择\(i\)以后所能形成的满足条件的子序列的最大值 \(minx[i]\)表示\(i\)能转换为的最小值 \(maxx[i]\)表示\(i\)能转换为的最大值 于是转移的条件显然了: \(i>j\) \(minx[i]>=a[j]\) \(a[i]>=maxx[i]\) 对于暴力直接枚举j转移就好了,但却只有50分,想想正解. 条件很明显是三维偏序问题啊.我们可以随便用一些数据结构: 如: cdq分治

洛谷P4093 [HEOI2016/TJOI2016]序列

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

洛谷 P4093 [HEOI2016/TJOI2016]序列

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

题解 P4093 【[HEOI2016/TJOI2016]序列】

这道题原来很水的? noteskey 一开始以为是顺序的 m 个修改,然后选出一段最长子序列使得每次修改后都满足不降 这 TM 根本不可做啊! 于是就去看题解了,然后看到转移要满足的条件的我发出了黑人问号... 然后才发现原来是求的子序列是满足任意一次修改后不降... 于是列出两(san)个条件式子,就可以 CDQ 切掉了 QWQ \(j<i\) \(a_j<min_i\) \(max_j<a_i\) 这里的 max 和 min 就是某个位置上出现过的最 大/小 值 watch out

luogu4093 [HEOI2016/TJOI2016]序列

因为一个变化只会变化一个值,所以 \(dp[i]=max(dp[j])+1,j<i,maxval_j \leq a[i], a[j] \leq minval_i\) 发现跟二维数点问题挺像,树状数组套线段树爽一爽. #include <iostream> #include <cstdio> using namespace std; int n, m, zdz[100005], zxz[100005], a[100005], uu, vv, cnt, dp[100005], a