1. 程式人生 > >[SCOI2010]序列操作

[SCOI2010]序列操作

href interval hup ret 左右 show 解決 也好 信息

嘟嘟嘟

這一看都知道,肯定是線段樹,只不過這個稍微有些復雜……

首先對於操作0和1都是很好辦的,比較簡單的區間修改。然後查詢區間多少個1,就是區間和,也好辦。

至於查詢連續個1,做過酒店的都知道怎麽辦,維護一個imax[now](imax是interval‘s max,不是電影……):區間連續的1是多少個,lmax[now]:從now區間的左端點開始有多少個連續的1,rmax[now]從右端點開始有多少連續個1,這樣區間合並的時候就是imax[now] = max(imax[now << 1], imax[now << 1 | 1], rmax[now << 1] + lmax[now << 1 | 1]).

查詢酒店那道題沒講。對於要找的區間[L, R],如果各有一部分在當前區間的左右兒子中,那麽分三種情況:1.在左兒子中,那麽遞歸下去。1.在右兒子中,同樣遞歸下去。3.最優解可能一部分在左兒子中,一部分在右兒子中,然而不能直接返回 t[now << 1].rmax +t[now << 1 |1].lmax,因為還有當前要找的區間的限制,因此是min(t[now << 1].rmax1, mid - L + 1) + min(t[now << 1| 1].lmax1, R - mid - 1 + 1)。然後這三種情況取max返回。

最後有解決的是區間反轉問題,也是開一個標記(我就叫lzy_re了),他的優先級是比區間修改的優先級低的,因為若果一個區間都改成了0 / 1,那麽這個區間之前的反轉就沒有用了,所以pushdown的時候,先把lazy往下傳,再把lzy_re往下傳。

這樣的話要維護區間最長的1和0了,這樣反轉的時候只用把1和0維護的信息交換即可。

一個技巧就是在pushup的時候,可以重定義 ‘+‘ 號,這樣用結構體的時候在結構體裏面寫就行,然後外面直接寫一個 t[now] = t[now << 1] + t[now << 1 | 1] 就行了。

具體的細節看代碼

技術分享圖片
  1 #include<cstdio>
  2 #include<iostream>
  3 #include<algorithm>
  4 #include<cmath>
  5 #include<cstring>
  6
#include<cstdlib> 7 #include<stack> 8 #include<queue> 9 #include<vector> 10 #include<cctype> 11 using namespace std; 12 #define enter puts("") 13 #define space putchar(‘ ‘) 14 #define Mem(a) memset(a, 0, sizeof(a)) 15 typedef long long ll; 16 typedef double db; 17 const int INF = 0x3f3f3f3f; 18 const db eps = 1e-8; 19 const int maxn = 1e5 + 5; 20 inline ll read() 21 { 22 ll ans = 0; 23 char ch = getchar(), last = ; 24 while(!isdigit(ch)) {last = ch; ch = getchar();} 25 while(isdigit(ch)) {ans = ans * 10 + ch - 0; ch = getchar();} 26 if(last == -) ans = -ans; 27 return ans; 28 } 29 inline void write(ll x) 30 { 31 if(x < 0) putchar(-), x = -x; 32 if(x >= 10) write(x / 10); 33 putchar(x % 10 + 0); 34 } 35 36 int n, m; 37 38 struct Tree 39 { 40 int l, r, sum, lzy, lzy_re; 41 int lmax0, rmax0, imax0, lmax1, rmax1, imax1; 42 Tree operator + (const Tree& other)const 43 { 44 Tree ret; 45 ret.l = l; ret.r = other.r; //這個別忘了 46 ret.sum = sum + other.sum; 47 ret.lzy = -1; ret.lzy_re = 0; //因為pushup操作在pushdown之後,所以此時所有標記已清空 48 ret.lmax0 = lmax0; 49 if(lmax0 == r - l + 1) ret.lmax0 += other.lmax0; //以下是維護區間最長的0和1,思路詳見酒店那道題 50 ret.rmax0 = other.rmax0; 51 if(other.rmax0 == other.r - other.l + 1) ret.rmax0 += rmax0; 52 ret.imax0 = max(rmax0 + other.lmax0, max(imax0, other.imax0)); 53 ret.lmax1 = lmax1; 54 if(lmax1 == r - l + 1) ret.lmax1 += other.lmax1; 55 ret.rmax1 = other.rmax1; 56 if(other.rmax1 == other.r - other.l + 1) ret.rmax1 += rmax1; 57 ret.imax1 = max(rmax1 + other.lmax1, max(imax1, other.imax1)); 58 return ret; 59 } 60 }t[maxn << 2]; 61 62 void build(int L, int R, int now) 63 { 64 t[now].l = L; t[now].r = R; 65 if(L == R) 66 { 67 t[now].sum = read(); t[now].lzy = -1; t[now].lzy_re = 0; //lzy初值最好設成-1,因為每一個數是0 / 1,初值為0不好判斷 68 t[now].lmax0 = t[now].rmax0 = t[now].imax0 = t[now].sum ^ 1; 69 t[now].lmax1 = t[now].rmax1 = t[now].imax1 = t[now].sum; 70 return; 71 } 72 int mid = (L + R) >> 1; 73 build(L, mid, now << 1); 74 build(mid + 1, R, now << 1 | 1); 75 t[now] = t[now << 1] + t[now << 1 | 1]; 76 } 77 void pushdown(int now) 78 { 79 if(t[now].lzy != -1) //先傳區間修改 80 { 81 t[now << 1].sum = (t[now << 1].r - t[now << 1].l + 1) * t[now].lzy; //左區間 82 t[now << 1].lzy = t[now].lzy; 83 t[now << 1].lzy_re = 0; //他的左右兒子區間的反轉標記得清零 84 t[now << 1].lmax0 = t[now << 1].rmax0 = t[now << 1].imax0 = t[now << 1].lzy ? 0 : t[now << 1].r - t[now << 1].l + 1; 85 //剛開始寫成了 = t[now].sum * (t[now].lzy ^ 1),顯然不對呀…… 86 t[now << 1].lmax1 = t[now << 1].rmax1 = t[now << 1].imax1 = t[now << 1].sum; 87 88 t[now << 1 | 1].sum = (t[now << 1 | 1].r - t[now << 1 | 1].l + 1) * t[now].lzy; //右區間 89 t[now << 1 | 1].lzy = t[now].lzy; 90 t[now << 1 | 1].lzy_re = 0; 91 t[now << 1 | 1].lmax0 = t[now << 1 | 1].rmax0 = t[now << 1 | 1].imax0 = t[now << 1 | 1].lzy ? 0 : t[now << 1 | 1].r - t[now << 1 | 1].l + 1; 92 t[now << 1 | 1].lmax1 = t[now << 1 | 1].rmax1 = t[now << 1 | 1].imax1 = t[now << 1 | 1].sum; 93 t[now].lzy = -1; 94 } 95 if(t[now].lzy_re) 96 { 97 t[now << 1].sum = t[now << 1].r - t[now << 1].l + 1 - t[now << 1].sum; 98 t[now << 1].lzy_re ^= 1; 99 //註意一定要 ^ 1,而不是直接 = 1,因為很顯然的是反轉兩次就還是原狀。這就體現了lzy_re初值為0而不是-1的好處 100 swap(t[now << 1].lmax0, t[now << 1].lmax1); swap(t[now << 1].rmax0, t[now << 1].rmax1); 101 swap(t[now << 1].imax0, t[now << 1].imax1); //直接把維護最長的0和1這倆的信息交換就行 102 103 t[now << 1 | 1].sum = t[now << 1 | 1].r - t[now << 1 | 1].l + 1 - t[now << 1 | 1].sum; 104 t[now << 1 | 1].lzy_re ^= 1; //註意不是直接等於1 105 swap(t[now << 1 | 1].lmax0, t[now << 1 | 1].lmax1); swap(t[now << 1 | 1].rmax0, t[now << 1 | 1].rmax1); 106 swap(t[now << 1 | 1].imax0, t[now << 1 | 1].imax1); 107 t[now].lzy_re = 0; 108 } 109 } 110 void update_all(int L, int R, int now, bool d) 111 { 112 if(t[now].l == L && t[now].r == R) 113 { 114 t[now].sum = (R - L + 1) * d; 115 t[now].lzy = d; 116 t[now].lzy_re = 0; 117 t[now].lmax0 = t[now].rmax0 = t[now].imax0 = d ? 0 : R - L + 1; 118 t[now].lmax1 = t[now].rmax1 = t[now].imax1 = t[now].sum; 119 return; 120 } 121 pushdown(now); 122 int mid = (t[now].l + t[now].r) >> 1; 123 if(R <= mid) update_all(L, R, now << 1, d); 124 else if(L > mid) update_all(L, R, now << 1 | 1, d); 125 else update_all(L, mid, now << 1, d), update_all(mid + 1, R, now << 1 | 1, d); 126 t[now] = t[now << 1] + t[now << 1 | 1]; 127 } 128 void update_re(int L, int R, int now) 129 { 130 if(t[now].l == L && t[now].r == R) 131 { 132 t[now].sum = R - L + 1 - t[now].sum; 133 t[now].lzy_re ^= 1; 134 swap(t[now].lmax0, t[now].lmax1); swap(t[now].rmax0, t[now].rmax1); 135 swap(t[now].imax0, t[now].imax1); 136 return; 137 } 138 pushdown(now); 139 int mid = (t[now].l + t[now].r) >> 1; 140 if(R <= mid) update_re(L, R, now << 1); 141 else if(L > mid) update_re(L, R, now << 1 | 1); 142 else update_re(L, mid, now << 1), update_re(mid + 1, R, now << 1 | 1); 143 t[now] = t[now << 1] + t[now << 1 | 1]; 144 } 145 int query_sum(int L, int R, int now) 146 { 147 if(t[now].l == L && t[now].r == R) return t[now].sum; 148 pushdown(now); 149 int mid = (t[now].l + t[now].r) >> 1; 150 if(R <= mid) return query_sum(L, R, now << 1); 151 else if(L > mid) return query_sum(L, R, now << 1 | 1); 152 else return query_sum(L, mid, now << 1) + query_sum(mid + 1, R, now << 1 | 1); 153 } 154 int query_max(int L, int R, int now) 155 { 156 if(t[now].l == L && t[now].r == R) return t[now].imax1; 157 pushdown(now); 158 int mid = (t[now].l + t[now].r) >> 1; 159 if(R <= mid) return query_max(L, R, now << 1); 160 else if(L > mid) return query_max(L, R, now << 1 | 1); 161 else //最大值分三種情況 162 { 163 int ret1 = max(query_max(L, mid, now << 1), query_max(mid + 1, R, now << 1 | 1)); //分別在左右區間 164 int ret2 = min(t[now << 1].rmax1, mid - L + 1) + min(t[now << 1| 1].lmax1, R - mid - 1 + 1); //在兩區間都有 165 //剛開始寫成 min(t[now << 1].rmax1, L) + min(t[now << 1| 1].lmax1, R),因為比較的是長度,而L, R是端點,自然就不對了 166 return max(ret1, ret2); 167 } 168 } 169 170 int main() 171 { 172 n = read(); m = read(); 173 build(0, n - 1, 1); 174 for(int i = 1; i <= m; ++i) 175 { 176 int d = read(), L = read(), R = read(); 177 if(d == 0) update_all(L, R, 1, 0); 178 else if(d == 1) update_all(L, R, 1, 1); 179 else if(d == 2) update_re(L, R, 1); 180 else if(d == 3) write(query_sum(L, R, 1)), enter; 181 else write(query_max(L, R, 1)), enter; 182 } 183 return 0; 184 }
View Code

[SCOI2010]序列操作