[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\n", 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\n", 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 }