hzwer的9題 https://loj.ac/problem/6277 https://loj.ac/problem/6278 https://loj.ac/problem/6279 https://loj.ac/problem/6280 https://loj.ac/problem/6281 https://loj.ac/problem/6282 https://loj.ac/problem/6283 https://loj.ac/problem/6284 https://loj.ac/problem/6285
\(\text{例題會稍後放上}\)node
\(\huge \text{分塊是一種優雅的暴力}\)ios
\(\text{大概思想是這樣的:維護$\sqrt(n)$個塊 查詢的時候是查詢區間內的塊 若是有的部分不滿一個塊就用枚舉 常數較小}\)c++
\(\text{對於上面這個說明 我舉個簡單的例子: 假設n = 10000 那麼 每一個塊就是$\sqrt n$ = 100 每一個塊是}\)git
1~100 101~200 ... 9901~10000
\(\text{假設要查詢 2 ~ 999的值}\)數組
\(\text{你只須要維護每一個 大小 爲 $\sqrt n$ 的塊 也就是 大小 爲 100 的塊 而後對$101$~$200$ - $801$~$900$這幾個塊查詢塊的值}\)
\(\text{而後暴力 $2$~$100$ 和 $901$ ~ $999$ 的值 }\)ui
\(\text{之因此稱爲優雅的暴力 分塊把樸素暴力的 $2$ ~ $999$ 改爲了 $200$ 複雜度左右的暴力}\)spa
\(\text{我的以爲 $2$~$999$是最壞狀況}\)c++11
\(\text{若是查詢 $1$~$1000$ 那麼分塊的優點就出來了}\)code
\(\text{我的以爲比線段樹簡單 思想比線段樹容易可是代碼長(小聲}\)排序
\(\text{通常來說的話 分塊和線段樹就差一個O2(由於線段樹的常數大}\)
\(\text{分塊的預處理是 將每個數字存入一個塊中 複雜度是O(N)}\)
\(\text{而後區間修改查詢什麼的 最多不超過}\) \(2 \ \sqrt(n)\)
\(\text{證實:一個塊的大小是}\)\(\sqrt(n)\) \(\text{那麼多出來的左區間和右區間的最壞狀況是 ($\sqrt(n)$) 因此易證}\)
\(\text{分塊的時間複雜度大概就是 }\) \(\theta (N + q * \sqrt(N))\)
(n指序列長度 q指查詢修改的操做次數)
\(\text{以上就是一個基本的分塊思想 簡單講 分塊比線段樹容易實現}\)
\(\text{分塊1}\)
分塊1是區間修改 單點查詢
咱們用一個數組維護塊
若是區間修改的時候 包含這個塊 那麼能夠把這個塊加上須要修改的值 若是是多出來的就直接暴力修改了
而後查詢的時候輸出所在塊修改的值 和 自己的值
// Isaunoya #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize("Ofast") #include<bits/stdc++.h> using namespace std ; const int N = 50000 + 5 ; const int Bl = 300 + 5 ; struct node { int l , r ; int add ; } ; int n ; int a[N] ; node atag[Bl] ; int bl[N] ; int unt ; inline void change(int l , int r , int c) { for(register int i = l ; i <= min(bl[l] * unt , r) ; i ++) a[i] += c ; if(bl[l] != bl[r]) for(register int i = (bl[r] - 1) * unt + 1 ; i <= r ; i ++) a[i] += c ; for(register int i = bl[l] + 1 ; i <= bl[r] - 1 ; i ++) atag[i].add += c ; } signed main() { ios::sync_with_stdio(false) ; cin >> n ; for(register int i = 1 ; i <= n ; i ++) cin >> a[i] ; unt = sqrt(n) ; for(register int i = 1 ; i <= n ; i ++) { bl[i] = (i - 1) / unt + 1 ; } for(register int i = 1 ; i <= n ; i ++) { int opt ; cin >> opt ; if(opt == 0) { int l , r , c ; cin >> l >> r >> c ; change(l , r , c) ; } else { int l , r , c ; cin >> l >> r >> c ; cout << a[r] + atag[bl[r]].add << endl ; } } return 0 ; }
\(\text{分塊2}\)
分塊二求的是區間修改 區間查詢小於\(c^2\)的最大數
vector 每次修改完重構一下左右塊就好了
#include <bits/stdc++.h> using namespace std; typedef long long LL; inline int read() { register int x = 0; register int f = 1; register char c; #define gc c = getchar() while (isspace(gc)) ; c == '-' ? gc, f = -1 : 0; while (x = (x << 3) + (x << 1) + (c & 15), isdigit(gc)) ; return x * f; } const int N = 50000 + 5; const int Bl = 300 + 5; int n; int a[N]; struct node { int add; std::vector<int> v; }; node atag[Bl]; int unt; int bl[N]; inline void reset(int x) { atag[x].v.clear(); for (register int i = (x - 1) * unt + 1; i <= min(x * unt, n); i++) atag[x].v.push_back(a[i]); sort(atag[x].v.begin(), atag[x].v.end()); return; } inline void change(int l, int r, int c) { for (register int i = l; i <= min(bl[l] * unt, r); i++) a[i] += c; reset(bl[l]); if (bl[l] != bl[r]) { for (register int i = (bl[r] - 1) * unt + 1; i <= r; i++) a[i] += c; reset(bl[r]); } for (register int i = bl[l] + 1; i <= bl[r] - 1; i++) atag[i].add += c; } inline int query(int l, int r, LL c) { int ans = 0; for (register int i = l; i <= min(bl[l] * unt, r); i++) if (a[i] + atag[bl[l]].add < c) ans++; if (bl[l] != bl[r]) { for (register int i = (bl[r] - 1) * unt + 1; i <= r; i++) if (a[i] + atag[bl[r]].add < c) ans++; } for (register int i = bl[l] + 1; i <= bl[r] - 1; i++) { int s = c - atag[i].add; ans += lower_bound(atag[i].v.begin(), atag[i].v.end(), s) - atag[i].v.begin(); } return ans; } signed main() { n = read(); unt = sqrt(n); for (register int i = 1; i <= n; i++) a[i] = read(); for (register int i = 1; i <= n; i++) bl[i] = (i - 1) / unt + 1; for (register int i = 1; i <= n; i++) { atag[bl[i]].v.push_back(a[i]); } for (register int i = 1; i <= bl[n]; i++) { sort(atag[i].v.begin(), atag[i].v.end()); } for (register int i = 1; i <= n; i++) { int opt = read(), L = read(), R = read(), c = read(); if (opt) printf("%lld\n", query(L, R, c * c)); else change(L, R, c); } return 0; }
\(\text{分塊3}\)
分塊3 區間修改 詢問區間內小於某個值 x的前驅即比x小的最大數
一樣使用一個set維護
而後用\(lower _ \ bound\)二分
(部分C++11內容
// Isaunoya #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize("Ofast") #pragma GCC diagnostic error "-std=c++11" #include <bits/stdc++.h> using namespace std; typedef long long LL; inline int read() { register int x = 0; register int f = 1; register char c; #define gc c = getchar() while (isspace(gc)) ; c == '-' ? gc, f = -1 : 0; while (x = (x << 3) + (x << 1) + (c & 15), isdigit(gc)) ; return x * f; } const int N = 100000 + 5; const int Bl = 400 + 5; int n; int a[N]; struct node { int add; }; node atag[Bl]; int unt; int bl[N]; set<int> st[Bl]; inline void change(int l, int r, int c) { for (register int i = l; i <= min(bl[l] * unt, r); i++) { st[bl[i]].erase(a[i]); a[i] += c; st[bl[i]].insert(a[i]); } if (bl[l] != bl[r]) { for (register int i = (bl[r] - 1) * unt + 1; i <= r; i++) { st[bl[i]].erase(a[i]); a[i] += c; st[bl[i]].insert(a[i]); } } for (register int i = bl[l] + 1; i <= bl[r] - 1; i++) atag[i].add += c; } inline int query(int l, int r, int c) { int ans = -1; for (register int i = l; i <= min(bl[l] * unt, r); i++) if (a[i] + atag[bl[l]].add < c) ans = max(a[i] + atag[bl[l]].add, ans); if (bl[l] != bl[r]) { for (register int i = (bl[r] - 1) * unt + 1; i <= r; i++) if (a[i] + atag[bl[r]].add < c) ans = max(a[i] + atag[bl[l]].add, ans); } for (register int i = bl[l] + 1; i <= bl[r] - 1; i++) { int s = c - atag[i].add; auto find = st[i].lower_bound(s); if (find == st[i].begin()) continue ; find--; ans = max(ans, *find + atag[i].add); } return ans; } signed main() { n = read(); unt = sqrt(n); for (register int i = 1; i <= n; i++) a[i] = read(); for (register int i = 1; i <= n; i++) bl[i] = (i - 1) / unt + 1; for (register int i = 1; i <= n; i++) { st[bl[i]].insert(a[i]); } for (register int i = 1; i <= n; i++) { int opt = read(), L = read(), R = read(), c = read(); if (opt) printf("%d\n", query(L, R, c)); else change(L, R, c); } return 0; }
\({\text{分塊4}}\)
分塊4是區間修改 區間求和
而後 區間和 % \(c+1\)
咱們考慮使用一個數組維護區間和 而後用一個數組維護整個塊的修改狀況
維護區間和指的是 暴力修改 的部分
// Isaunoya #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize("Ofast") #include<bits/stdc++.h> using namespace std ; #define int long long inline int read() { register int x = 0 ; register int f = 1 ; register char c ; #define gc c = getchar() while(isspace(gc)) ; c == '-' ? gc , f = -1 : 0 ; while(x = (x << 1) + (x << 3) + (c ^ 48) , isdigit(gc)) ; return x * f ; } int n ; const int N = 50000 + 5 ; int a[N] ; int bl[N] ; int sum[N] ; int atag[N] ; int unt ; inline void change(int l , int r , int c) { for(register int i = l ; i <= min(r , bl[l] * unt) ; i ++) { a[i] += c ; sum[bl[l]] += c ; } if(bl[l] != bl[r]) for(register int i = (bl[r] - 1) * unt + 1 ; i <= r ; i ++) { a[i] += c ; sum[bl[r]] += c ; } for(register int i = bl[l] + 1 ; i <= bl[r] - 1 ; i ++) atag[i] += c ; return ; } inline int query(int l , int r , int c) { int ans = 0 ; for(register int i = l ; i <= min(r , bl[l] * unt) ; i ++) { ans += a[i] + atag[bl[l]] ; ans %= c ; } if(bl[l] != bl[r]) for(register int i = (bl[r] - 1) * unt + 1 ; i <= r ; i ++) { ans += a[i] + atag[bl[r]] ; ans %= c ; } for(register int i = bl[l] + 1 ; i <= bl[r] - 1 ; i ++) ans = (ans + sum[i] + atag[i] * unt) % c ; return ans ; } signed main() { n = read() ; unt = sqrt(n) ; for(register int i = 1 ; i <= n ; i ++) a[i] = read() ; for(register int i = 1 ; i <= n ; i ++) bl[i] = (i - 1) / unt + 1 ; for(register int i = 1 ; i <= n ; i ++) { sum[bl[i]] += a[i] ; } for(register int i = 1 ;i <= n ; i ++) { int opt = read() ; if(opt == 0) { int l = read() , r = read() , c = read() ; change(l , r , c) ; } if(opt == 1) { int l = read() , r = read() , c = read() ; printf("%lld\n" , query(l , r , c + 1)) ; } } return 0 ; }
\(\text{分塊5}\)
分塊5是區間開方 而後區間查詢
對於每一個塊 大小最大是\(2^{31}\)次
\(log(31) ≈ 5\)
因此能夠得出一個塊最多開方6次
也就是最大是\(6n\)
因此對於區間開方 對一個塊進行開方 而後重構這個塊的總和
也就是能夠得出 大塊修改並用一個flg數組標記這個塊有沒有大於1的數字(若是大於1的話還能夠開方
這樣能夠避免不少次重複開方
那麼對於沒有完整塊的左右區間 咱們考慮先減掉原數字而後加上開方後的數字(對於乘法操做也是同樣的
區間查詢上面講過了(將塊的總和加上 而後暴力把左右區間求和)
// Isaunoya #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize("Ofast") #include<bits/stdc++.h> using namespace std ; #define int long long inline int read() { register int x = 0 ; register int f = 1 ; register char c ; #define gc c = getchar() while(isspace(gc)) ; c == '-' ? gc , f = -1 : 0 ; while(x = (x << 1) + (x << 3) + (c ^ 48) , isdigit(gc)) ; return x * f ; } int n ; const int N = 50000 + 5 ; int a[N] ; int bl[N] ; int flg[N] ; int sum[N] ; int unt ; inline void Reset(int x) { if(flg[x]) return ; flg[x] = 1 ; sum[x] = 0 ; for(register int i = (x - 1) * unt + 1 ; i <= x * unt ; i ++) { a[i] = sqrt(a[i]) ; sum[x] += a[i] ; if(a[i] > 1) flg[x] = 0 ; } return ; } inline void change(int l , int r , int c) { for(register int i = l ; i <= min(bl[l] * unt , r) ; i ++) { sum[bl[l]] -= a[i] ; a[i] = sqrt(a[i]) ; sum[bl[l]] += a[i] ; } if(bl[l] != bl[r]) for(register int i = (bl[r] - 1) * unt + 1 ; i <= r ; i ++) { sum[bl[r]] -= a[i] ; a[i] = sqrt(a[i]) ; sum[bl[r]] += a[i] ; } for(register int i = bl[l] + 1 ; i <= bl[r] - 1 ; i ++) Reset(i) ; return ; } inline int query(int l , int r , int c) { int ans = 0 ; for(register int i = l ; i <= min(bl[l] * unt , r) ; i ++) { ans += a[i] ; } if(bl[l] != bl[r]) for(register int i = (bl[r] - 1) * unt + 1 ; i <= r ; i ++) ans += a[i] ; for(register int i = bl[l] + 1 ; i <= bl[r] - 1 ; i ++) ans += sum[i] ; return ans ; } signed main() { n = read() ; unt = sqrt(n) ; for(register int i = 1 ; i <= n ; i ++) a[i] = read() ; for(register int i = 1 ; i <= n ; i ++) bl[i] = (i - 1) / unt + 1 ; for(register int i = 1 ; i <= n ; i ++) { sum[bl[i]] += a[i] ; } for(register int i = 1 ; i <= n ; i ++) { int opt = read() ; if(opt == 0) { int l , r , c ; l = read() ; r = read() ; c = read() ; change(l , r , c) ; } if(opt == 1) { int l , r , c ; l = read() ; r = read() ; c = read() ; printf("%lld\n" , query(l , r , c)) ; } } return 0 ; }
\(\text{分塊6}\)
分塊6是能夠區間插入一個數字 而後查詢某個位置
(其實用不到分塊 \(vector\) 能夠直接作)
只是由於這一題沒有區間查詢 因此能夠 \(vector\) 水過去
若是想好好學分塊就使用分塊的作法
當某個塊大於 20 個塊的時候 就重構塊
否則查詢的時候比較費勁 並且暴力複雜度比較高
// Isaunoya #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize("Ofast") #pragma GCC diagnostic error "-std=c++11" #include<bits/stdc++.h> using namespace std ; #define int long long inline int read() { register int x = 0 ; register int f = 1 ; register char c ; #define gc c = getchar() while(isspace(gc)) ; c == '-' ? gc , f = -1 : 0 ; while(x = (x << 1) + (x << 3) + (c ^ 48) , isdigit(gc)) ; return x * f ; } int n ; int unt ; const int N = 200000 + 5 ; int a[N] ; std::vector< int > v[N] ; int st[N] ; int top = 0 ; int m = 0 ; pair < int , int > query(int b) { int x = 1 ; for ( ; b > v[x].size() ; ) b -= v[x ++].size() ; return make_pair(x , b - 1) ; } inline void Rebuild() { top = 0 ; for(register int i = 1 ; i <= m ; i ++) { for(auto j : v[i]) st[++ top] = j ; v[i].clear() ; } int blo = sqrt(top) ; for(register int i = 1 ; i <= top ; i ++) { v[(i - 1) / blo + 1].push_back(st[i]) ; } m = (top - 1) / blo + 1 ; } inline void Insert(int x , int y) { pair < int , int > p = query(x) ; v[p.first].insert(v[p.first].begin() + p.second , y) ; if(v[p.first].size() > 20 * unt) Rebuild() ; } signed main() { n = read() ; unt = sqrt(n) ; for(register int i = 1 ; i <= n ; i ++) a[i] = read() ; for(register int i = 1 ; i <= n ; i ++) v[(i - 1) / unt + 1].push_back(a[i]) ; m = (n - 1) / unt + 1 ; for(register int i = 1 ; i <= n ; i ++) { int opt = read() ; if(opt == 0) { int l = read() , r = read() , c = read() ; Insert(l , r) ; } if(opt == 1) { int l = read() , r = read() , c = read() ; pair < int , int > p = query(r) ; printf("%d\n" , v[p.first][p.second]) ; } } return 0 ; }
\(\text{分塊7}\)
分塊7是一題 \(\text{區間乘法 區間加法 單點查詢}\) 的題目
那麼咱們只須要維護一下區間的值 乘法的值 加法的值就能夠
若是乘法那麼就須要把加法的值乘上一個值
不過在每次修改的時候 須要\(\text{重構這個不完整的塊所在的塊}\)
(好拗口 反正就是在\(左,右區間\)所在的塊 而後把乘的值歸1 而後加法的值清0
這樣的話複雜度仍然仍是 \(\sqrt n\)
根據這個思路的話 我寫了一個區間查詢的
我告訴我旁邊的人:知道什麼叫作單點查詢嗎
-- \(query(b,b)\)
// Isaunoya #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize("Ofast") #pragma GCC diagnostic error "-std=c++11" #include<bits/stdc++.h> using namespace std ; #define int long long #define rep(i , j , n) for(register int i=j;i<=n;i++) #define Rep(i , j , n) for(register int i=j;i>=n;i--) #define gc c = getchar() #define int long long inline int read() { register int x = 0 , f = 1 ; register char c ; while(isspace(gc)) ; c == '-' ? f = -1 , gc : 0 ; while((x *= 10) += (c ^ 48) , isdigit(gc)) ; return x * f ; } using namespace std ; int n ; const int N = 100000 + 5 ; const int Unt = 2000 ; const int p = 10000 + 7 ; int a[N] ; int unt ; int block[N] ; int sum[Unt] ; int mul[Unt] ; int size[Unt] ; int ans[Unt] ; inline void reset(int x) { for(register int i=(x - 1) * unt + 1 ; i <= min(n , x * unt) ; i ++) { a[i] = (a[i] * mul[x] + sum[x]) % p ; } sum[x] = 0 ; mul[x] = 1 ; } inline void change_mul(int l , int r , int x) { reset(block[l]) ; for(register int i=l;i<=min(r , block[l] * unt) ; i ++) { ans[block[l]] -= a[i] ; a[i] *= x ; a[i] %= p ; ans[block[l]] += a[i] ; ans[block[l]] %= p ; } if(block[l] != block[r]) { reset(block[r]) ; for(register int i=(block[r] - 1) * unt + 1 ; i <= r ; i ++) { ans[block[r]] -= a[i] ; a[i] *= x ; a[i] %= p ; ans[block[r]] += a[i] ; ans[block[r]] %= p ; } for(register int i=block[l] + 1 ; i <= block[r] - 1 ; i ++) { sum[i] *= x ; sum[i] %= p ; mul[i] *= x ; mul[i] %= p ; ans[i] *= x ; ans[i] %= p ; } } } inline void change_sum(int l , int r , int x) { reset(block[l]) ; for(register int i=l ; i <= min(r , block[l] * unt) ; i ++) { a[i] += x ; a[i] %= p ; ans[block[l]] += x ; } if(block[l] != block[r]) { reset(block[r]) ; for(register int i = (block[r] - 1) * unt + 1 ; i <= r ; i ++) { a[i] += x ; a[i] %= p ; ans[block[r]] += x ; } for(register int i=block[l] + 1 ; i <= block[r] - 1 ; i ++) { sum[i] += x ; sum[i] %= p ; ans[i] += x * size[i] ; } } } inline int Query(int l , int r) { int Ans = 0 ; for(register int i=l;i<=min(r , block[l] * unt) ; i ++) Ans += (a[i] * mul[block[l]] + sum[block[l]] ) % p ; if(block[l] != block[r]) { for(register int i = (block[r] - 1) * unt + 1 ; i <= r ; i ++) Ans += (a[i] * mul[block[r]] + sum[block[r]] ) % p ; for(register int i = block[l] + 1 ; i <= block[r] - 1 ; i ++) Ans += ans[i] ; } return Ans % p ; } signed main() { n = read() ; unt = sqrt(n) ; for(register int i=1;i<=n;i++) a[i] = read() ; for(register int i=1;i<=n;i++) { block[i] = (i - 1) / unt + 1 ; ans[block[i]] += a[i] ; size[block[i]] ++ ; } for(register int i=1;i<=block[n];i++) mul[i] = 1 ; for(register int i=1;i<=n;i++) { int opt = read() ; if(opt == 0) { int a = read() , b = read() , c = read() ; change_sum(a , b , c) ; } if(opt == 1) { int a = read() , b = read() , c = read() ; change_mul(a , b , c) ; } if(opt == 2) { int a = read() , b = read() , c = read() ; printf("%lld\n" , Query(b , b)) ; } } return 0 ; }
\(\text{分塊8}\)
分塊8的話是一個 查詢區間有多少個\(c\) 並把整個區間改爲 \(c\)
一樣咱們考慮判重 把每一個塊用一個\(flg\)數組記錄當前值
而後對於左右區間的修改 把左右區間所在的塊重構成以前的\(flg\)
而後修改爲c 再判斷左右區間有多少個c
那麼對於\(\text{完整的塊}\) 咱們只須要 加上一個塊的長度
這樣就能夠作到判重的效果了
對於 \(\text{完整的塊}\) 咱們只須要修改當前塊的\(flg\)值就能夠了
// Isaunoya #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize("Ofast") #pragma GCC diagnostic error "-std=c++11" #include<bits/stdc++.h> using namespace std ; #define int long long #define rep(i , j , n) for(register int i=j;i<=n;i++) #define Rep(i , j , n) for(register int i=j;i>=n;i--) #define gc c = getchar() #define int long long inline int read() { register int x = 0 , f = 1 ; register char c ; while(isspace(gc)) ; c == '-' ? f = -1 , gc : 0 ; while((x *= 10) += (c ^ 48) , isdigit(gc)) ; return x * f ; } const int N = 100000 + 5 ; int n ; int a[N] ; int unt ; int flg[N] ; int bl[N] ; inline void reset(int x) { if(flg[x] == -1) return ; for(register int i = (x - 1) * unt + 1 ; i <= x * unt ; i ++) a[i] = flg[x] ; flg[x] = -1 ; } inline int solve(int l , int r , int c) { int ans = 0 ; reset(bl[l]) ; for(register int i = l ; i <= min(bl[l] * unt , r) ; i ++) { if(a[i] != c) a[i] = c ; else ans ++ ; } if(bl[l] != bl[r]) { reset(bl[r]) ; for(register int i = (bl[r] - 1) * unt + 1 ; i <= r ; i ++) { if(a[i] != c) a[i] = c ; else ans ++ ; } } for(register int i = bl[l] + 1 ; i <= bl[r] - 1 ; i ++) { if(flg[i] != -1) { if(flg[i] != c) flg[i] = c ; else ans += unt ; } else { for(register int j = (i - 1) * unt + 1 ; j <= i * unt ; j ++) if(a[j] != c) a[j] = c ; else ans ++ ; flg[i] = c ; } } return ans ; } signed main() { n = read() ; unt = sqrt(n) ; for(register int i = 1 ; i <= n ; i ++) a[i] = read() ; for(register int i = 1 ; i <= n ; i ++) bl[i] = (i - 1) / unt + 1 ; for(register int i = 1 ; i <= bl[n] ; i ++) flg[i] = -1 ; for(register int i = 1 ; i <= n ; i ++) { int l = read() , r = read() , c = read() ; printf("%lld\n" , solve(l , r , c)) ; } return 0 ; }
\(\text{分塊9}\)
前置知識:離線莫隊
分塊9的話我沒想到怎麼在線作
就是按每次查詢的 \(l\) 排序 而後瞎搞
(大概是一個莫隊的思想
// Isaunoya #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize("Ofast") #pragma GCC diagnostic error "-std=c++11" #include <bits/stdc++.h> using namespace std; #define rep(i, j, n) for (register int i = j; i <= n; i++) #define Inc(i, j, n) for (register int i = j; i <= n; i++) #define Rep(i, j, n) for (register int i = j; i >= n; i--) inline int read() { register int x = 0, f = 1; register char c; #define gc c = getchar() while (isspace(gc)) ; c == '-' ? f = -1, gc : 0; while ((x *= 10) += (c ^ 48), isdigit(gc)) ; return x * f; } const int N = 1e5; int n, siz, k[N + 10], bl[N + 10]; struct Que { int L, r, idx; } q[N + 10]; bool cmp(const Que &A, const Que &B) { return bl[A.L] ^ bl[B.L] ? A.L < B.L : A.r < B.r; } inline void init() { n = read() ; Inc(i, 1, n) k[i] = read() ; Inc(i, 1, n) { int x, y; x = read() , y = read() ; q[i] = (Que) { x, y, i }; } siz = sqrt(n); Inc(i, 1, n) bl[i] = (i - 1) / siz + 1; sort(q + 1, q + 1 + n, cmp); } int rp[N + 10]; inline void disc() { int tmp[N + 10]; Inc(i, 1, n) tmp[i] = k[i]; sort(tmp + 1, tmp + 1 + n); int len = unique(tmp + 1, tmp + 1 + n) - tmp - 1; Inc(i, 1, n) rp[i] = lower_bound(tmp + 1, tmp + 1 + len, k[i]) - tmp; } int cur_ans, cur_num, Ans[N + 10], num[N + 10]; inline void addr(int x) { ++num[rp[x]]; if (num[rp[x]] >= cur_num) { if (num[rp[x]] == cur_num && k[x] < cur_ans) cur_ans = k[x]; else if (num[rp[x]] > cur_num) cur_ans = k[x], cur_num = num[rp[x]]; } } inline void addl(int x, int &ans, int &Num) { ++num[rp[x]]; if (num[rp[x]] >= Num) { if (num[rp[x]] == Num && k[x] < ans) ans = k[x]; else if (num[rp[x]] > Num) ans = k[x], Num = num[rp[x]]; } } inline void remove(int x) { --num[rp[x]]; } inline void solv() { int L, r, lim; Inc(i, 1, n) { if (bl[q[i].L] ^ bl[q[i - 1].L]) { memset(num, 0, sizeof(num)); L = lim = bl[q[i].L] * siz + 1; r = bl[q[i].L] * siz; cur_ans = cur_num = 0; } if (bl[q[i].L] == bl[q[i].r]) { int ans, nownum = 0; Inc(j, q[i].L, q[i].r)++ num[rp[j]]; Inc(j, q[i].L, q[i].r) if (num[rp[j]] >= nownum) { if (num[rp[j]] == nownum && k[j] < ans) ans = k[j]; else if (num[rp[j]] > nownum) ans = k[j], nownum = num[rp[j]]; } Inc(j, q[i].L, q[i].r)-- num[rp[j]]; Ans[q[i].idx] = ans; continue; } while (r < q[i].r) addr(++r); int now = cur_ans, nownum = cur_num; while (L > q[i].L) addl(--L, now, nownum); Ans[q[i].idx] = now; while (L < lim) remove(L++); } Inc(i, 1, n) printf("%d\n" , Ans[i]) ; } signed main() { init(); disc(); solv(); return 0; }
\(\text{例題}\)
https://loj.ac/problem/10117
\(\text{LOJ10117}\)
這題就是一個裸的樹狀數組 可是能夠用分塊作 好像更方便呢
// Isaunoya #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize("Ofast") #include<bits/stdc++.h> #define rep(i , j , n) for(register int i = j ; i <= n ; i ++) #define Rep(i , j , n) for(register int i = j ; i >= n ; i --) #define to(u) for(register int i = head[u] ; i ; i = edge[i].nxt) inline int read() { register int x = 0 , f = 1 ; register char c ; #define gc c = getchar() while(isspace(gc)) ; c == '-' ? f = -1 , gc : 0 ; while(x = (x << 1) + (x << 3) + (c & 15) , isdigit(gc)) ; return x * f ; } using namespace std ; int n ; const int N = 1e5 + 10 ; int a[N] ; int atag[N] ; int bl[N] ; int unt ; inline void change(int l , int r) { for(register int i = l ; i <= min(bl[l] * unt , r) ; i ++) a[i] ^= 1 ; if(bl[l] != bl[r]) for(register int i = (bl[r] - 1) * unt + 1 ; i <= r ; i ++) a[i] ^= 1 ; for(register int i = bl[l] + 1 ; i <= bl[r] - 1 ; i ++) atag[i] ^= 1 ; return ; } signed main() { // freopen(".in" , "r" , stdin) ; freopen(".out" , "w" , stdout) ; n = read() ; unt = sqrt(n) ; for(register int i = 1 ; i <= n ; i ++) bl[i] = (i - 1) / unt + 1 ; for(register int q = read() ; q -- ; ) { int opt = read() ; if(opt & 1) { int l = read() , r = read() ; change(l , r) ; } else { int x = read() ; printf("%lld\n" , a[x] ^ atag[bl[x]]) ; } } return 0 ; }
https://www.luogu.org/problem/P3870 https://www.luogu.org/problem/P2574 // 雙倍經驗 https://www.luogu.org/problem/P2801