zoukankan      html  css  js  c++  java
  • [JXOI2017]颜色 线段树扫描线 + 单调栈

    ~~~题面~~~

    题解:

      首先题目要求删除一些颜色,换个说法就是要求保留一些颜色,那么观察到,如果我们设ll[i]和rr[i]分别表示颜色i出现的最左边的那个点和最右边的那个点,那么题目就是在要求我们选出的区间要满足区间[l, r]内所有颜色的max(rr[i]) <= r,并且min(ll[i]) >= l. 因为是区间相关的问题,又涉及到左右端点,因此我们考虑扫描线,那么考虑如何维护它。

      因为每个颜色的ll[i]和rr[i]可以看做构成了一个区间,那么现在已经进入线段树的节点就分2种情况。

      1,区间右端点超过当前右端点:

        

      我们找到离当前右端点最近的点x,使得它代表的区间和右端点关系如上图所示,那么显然这个点x以及它之前的左端点都是不能取的,又因为这个点x是离当前右端点最近的满足条件的点,所以这个点之后都不会因为这个条件而产生冲突,即在这个点后面的,在当前右端点前面的,都满足了右端点的限制。那么我们只需要再满足左端点的限制,然后查询(x, i)对答案的贡献,其中i是当前枚举的右端点。那么我们如何找这个点呢?

      观察到一个性质,在后面出现的点的右端点>= 前面出现的点的右端点 的情况下,在后面出现的肯定会更优;因此我们只需要维护一个右端点单调递减的单调栈即可,如果有一个右端点更右出现了,那么肯定会比之前右端点比它小(相等)的点更优,但是不能弹掉右端点比它大的,因为随着右端点的增大,可能这个点就失效了,但之前右端点比它大的点还是有效的。    

      2,区间右端点不超过当前右端点。

        

      对于这种情况而言,显然我们要么把这个区间全部取了,要么一点都不取。因此不合法的左端点就是(ll, rr],把这段赋0即可。观察到因为我们是赋0,不是-1,所以无法撤销,但是这是没有关系的,因此如果在当前右端点下,这个区间已经不超过它了,那么以后随着右端点的增大,就更不可能超过了,因此不需要撤回。同时也正是因为无法撤回,所以上面那种情况需要单调栈而不是直接修改,因为上面那种情况,随着右端点的增大,是会变成第二种情况的。

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define R register int
      4 #define AC 301000
      5 #define ac 1200100
      6 #define LL long long
      7 
      8 /*用栈来维护查询的区间(因为每次的区间不同,而且都只要修改前缀,所以完全不用每次修改
      9 (修改了就无法转移到下一个右端点了,因为不满足区间减法,无法撤销),
     10 只需要查询指定区间内的就可以了*/
     11 
     12 int n, tot, T;
     13 LL ans;
     14 int ll[AC], rr[AC], color[AC];
     15 int s[AC], top;//
     16 int tree[ac], lazy[ac], l[ac], r[ac];//线段树
     17 struct co{
     18     int color, id;
     19 }p[AC];
     20 
     21 struct seg{
     22     int l, r;
     23 }line[AC];
     24 
     25 bool z[AC];
     26  
     27 inline int read()
     28 {
     29     int x = 0;char c = getchar();
     30     while(c > '9' || c < '0') c = getchar();
     31     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
     32     return x;
     33 }
     34 
     35 inline bool cmp1(seg a, seg b)
     36 {
     37     return a.r < b.r;
     38 }
     39 
     40 inline bool cmp(co a, co b)
     41 {
     42     if(a.color != b.color) return a.color < b.color;
     43     else return a.id < b.id;
     44 }
     45 
     46 inline void pushdown(int x)
     47 {
     48     if(l[x] != r[x] && lazy[x])
     49     {
     50         int ll = x * 2, rr = ll + 1;
     51         tree[ll] = tree[rr] = lazy[x] = 0;
     52         lazy[ll] = lazy[rr] = 1;
     53     }
     54 }
     55 
     56 inline void update(int x)
     57 {
     58     tree[x] = tree[x * 2] + tree[x * 2 + 1];
     59 }
     60 
     61 void build(int x, int ll, int rr)
     62 {
     63     l[x] = ll, r[x] = rr, lazy[x] = 0;
     64     if(ll == rr) {tree[x] = 1; return ;}
     65     int mid = (ll + rr) >> 1;
     66     build(x * 2, ll, mid), build(x * 2 + 1, mid + 1, rr);
     67     update(x);
     68 }
     69 
     70 void change(int x, int ll, int rr)
     71 {
     72     pushdown(x);
     73     if(l[x] == ll && r[x] == rr) {tree[x] = 0, lazy[x] = 1; return ;}
     74     int mid = (l[x] + r[x]) >> 1;
     75     if(rr <= mid) change(x * 2, ll, rr);
     76     else if(ll > mid) change(x * 2 + 1, ll, rr);
     77     else change(x * 2, ll, mid), change(x * 2 + 1, mid + 1, rr);
     78     update(x);
     79 }
     80 
     81 void find(int x, int ll, int rr)
     82 {
     83     pushdown(x);
     84     if(l[x] == ll && r[x] == rr){ans += tree[x]; return ;}
     85     int mid = (l[x] + r[x]) >> 1;
     86     if(rr <= mid) find(x * 2, ll, rr);
     87     else if(ll > mid) find(x * 2 + 1, ll, rr);
     88     else find(x * 2, ll, mid), find(x * 2 + 1, mid + 1, rr);
     89     update(x);
     90 }
     91 
     92 void pre()
     93 {
     94     n = read();
     95     for(R i = 1; i <= n; i ++) color[i] = p[i].color = read(), p[i].id = i;
     96     sort(p + 1, p + n + 1, cmp);
     97     for(R i = 1; i <= n; i ++)
     98         if(p[i].color != p[i - 1].color) 
     99             rr[p[i - 1].color] = p[i - 1].id, ll[p[i].color] = p[i].id;
    100     rr[p[n].color] = p[n].id;    
    101     for(R i = 1; i <= n; i ++) 
    102         if(ll[i]) line[++tot] = (seg){ll[i], rr[i]};
    103     sort(line + 1, line + tot + 1, cmp1);
    104 }
    105 
    106 void init()
    107 {
    108     memset(ll, 0, sizeof(ll)), memset(rr, 0, sizeof(rr));
    109     tot = ans = top = 0;
    110 }
    111 
    112 void get()
    113 {
    114     int l = 1;
    115     for(R i = 1; i <= n; i ++)//不断扩大右端点
    116     {
    117         //printf("!!!%d
    ", i);
    118         while(top && rr[color[i]] >= rr[color[s[top]]]) -- top;//如果一个点在栈顶右侧,且右端点大于等于栈顶,那么它肯定更优。    
    119         s[++top] = i;//栈里面存颜色就够了, ,,,不,,,还是需要存下标
    120         while(top && rr[color[s[top]]] <= i) -- top;//去掉不合法的情况
    121         for(; line[l].r <= i && l <= tot; ++ l)//error!!!这里要用tot,不然的话用n可能会用到一些未被覆盖的,来自前面的数据的区间
    122             if(line[l].l < line[l].r) change(1, line[l].l + 1, line[l].r);
    123         if(s[top] + 1 <= i) find(1, s[top] + 1, i); //要有合法的情况才查询,否则没有必要查询
    124     }
    125     printf("%lld
    ", ans);
    126 }
    127 
    128 void work()
    129 {
    130     T = read();
    131     while(T --) init(), pre(), build(1, 1, n), get();
    132 }
    133 
    134 int main()
    135 {
    136 //    freopen("color7.in", "r", stdin);
    137     work();
    138 //    fclose(stdin);
    139     return 0;
    140 }
    View Code
  • 相关阅读:
    Oracle锁表与解锁 对象锁与解锁
    Unity3D开发之NGUI点击事件穿透响应处理
    Unity 3D 关于给APK包加广告的流程
    Unity 3D 粒子系统的一点经验
    Unity3D模型的细致纹理问题解决办法
    Unity 3D学习之 Prime31 Game Center插件用法
    Unity3D如何制作透贴和使用透贴模型
    NGUI的部分控件无法更改layer?
    关于Unity3D中Resources动态加载NGUI图片的方法
    关于NGUI的动态加载后的刷新显示问题,解决办法!!
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9821675.html
Copyright © 2011-2022 走看看