• [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
    ", __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
    ", ans);
    133 //    printf("time used... %lf
    ", (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 }
  • 相关阅读:
    git使用流程
    php5.3.*编译出现make: *** [ext/gd/libgd/gd_compat.lo] Error 1 解决方法 [转]
    linux用户和组的操作,vi编辑器的使用
    linux目录介绍
    linux和windows分区原理
    linux配置网络,配置lmap
    linux常用操作命令
    mysql数据库编程,内置函数,存储过程(循环插入)
    msql数据库触发器和连接mysql常用函数
    mysql数据库增加删除跟新,关联数据
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9249812.html
Copyright © 2020-2023  润新知