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

猜你喜欢

转载自www.cnblogs.com/ww3113306/p/9249812.html