CF1398F
CF1398F
Solution
我又來貢獻暴力做法了。。。聽說兩只log不可能過1e6?
有一個顯然的想法是:
我們先預處理一個aia_iai?表示第iii個位置的最長后綴長度,滿足該后綴中不同時存在0和1。
假設我們要求x=ix=ix=i的答案,那么一個位置jjj可以作為一輪的結束點當且僅當aj≥ia_j\geq iaj?≥i,而一個結束位置序列p1<p2<...<pkp_1<p_2<...<p_kp1?<p2?<...<pk?合法當且僅當每一個pjp_jpj?都是合法結束點并且相鄰兩個元素的差至少為iii(保證不重合)。
因此我們會貪心地從第一個滿足aj≥ia_j\geq iaj?≥i的jjj開始,每次找一個最小的j′j'j′,滿足j′≥j+ij'\geq j+ij′≥j+i且aj′≥ia_{j'}\geq iaj′?≥i,然后從jjj跳到j′j'j′,最終的答案就是跳的步數。
這顯然可以直接對aia_iai?建區間線段樹,維護區間內的maxmaxmax,線段樹上二分求出j′j'j′。
這個方法的時間復雜度為O(∑ansilog?n)O(\sum ans_i\;\log n)O(∑ansi?logn),而ansi≤?ni?ans_i\leq \lfloor\frac{n}{i}\rflooransi?≤?in??,因此總時間復雜度O(nln?nlog?n)O(n\ln n\log n)O(nlnnlogn),注意常數即可。
(PS:別用偷懶用set維護,常數起飛,實測接近線段樹的三倍)。
Code
char st[MAXN]; int mx[MAXN << 2], a[MAXN]; void build(int x, int l, int r) {if (l == r) { mx[x] = a[l]; return; }int mid = (l + r) >> 1;build(x << 1, l, mid);build(x << 1 | 1, mid + 1, r);mx[x] = max(mx[x << 1], mx[x << 1 | 1]); } int query(int x, int l, int r, int L, int y) {if (mx[x] < y) return -1;if (l == r) return l;int mid = (l + r) >> 1;if (L > mid) return query(x << 1 | 1, mid + 1, r, L, y);else {int t = query(x << 1, l, mid, L, y);if (t != -1) return t;return query(x << 1 | 1, mid + 1, r, mid + 1, y);} } signed main() { #ifndef ONLINE_JUDGEfreopen("a.in", "r", stdin); #endifint n, cnt0 = 0, cnt1 = 0;read(n), reads(st);for (int i = 1, l = 1; i <= n ; ++ i) {auto add = [&](char x, int y) {if (x == '0') cnt0 += y; if (x == '1') cnt1 += y;};add(st[i], 1);while (cnt0 && cnt1) add(st[l ++], -1);a[i] = i - l + 1;}build(1, 1, n);for (int i = 1; i <= n ; ++ i) {int cnt = 0, r = 1;for (; r <= n ;) {r = query(1, 1, n, r, i);if (r == -1) break;r += i;++ cnt;}print(cnt), putc(' ');}return 0; }Update 4.5 23:30
上面的代碼跑了1890ms1890ms1890ms,我們覺得它不夠優秀,于是冷靜分析一下它慢在哪里,發現全1的數據就可以把它卡滿,這是為什么呢?這是因為我們在長長的沒有0的段里面做了很多次query,這其實是不必要的,我們同樣可以預處理以每個位置開始的最長段。這樣就可以O(1)O(1)O(1)計算整個段的貢獻。
我們提交后發現它只跑了249ms249ms249ms!
理性分析一下它的時間復雜度,相當于整個序列被01分割為若干段,跳到一個段就可以O(1)O(1)O(1)計算答案并且O(log?n)O(\log n)O(logn)跳到下一個段,這樣每一段會有O(len)O(len)O(len)次貢獻(因為每一個長度大于iii的段才可能產生貢獻),每次貢獻為O(log?n)O(\log n)O(logn),因此總時間復雜度為O(nlog?n)O(n\log n)O(nlogn)!
Updated code
//線段樹部分同上 signed main() { #ifndef ONLINE_JUDGEfreopen("a.in", "r", stdin); #endifint n, cnt0 = 0, cnt1 = 0;read(n), reads(st);for (int i = 1, l = 1; i <= n ; ++ i) {auto add = [&](char x, int y) {if (x == '0') cnt0 += y; if (x == '1') cnt1 += y;};add(st[i], 1);while (cnt0 && cnt1) add(st[l ++], -1);a[i] = i - l + 1;}cnt0 = 0, cnt1 = 0;for (int i = n, r = n; i >= 1 ; -- i) {auto add = [&](char x, int y) {if (x == '0') cnt0 += y; if (x == '1') cnt1 += y;};add(st[i], 1);while (cnt0 && cnt1) add(st[r --], -1);b[i] = r - i;}build(1, 1, n);for (int i = 1; i <= n ; ++ i) {int cnt = 0, r = 1;for (; r <= n ;) {r = query(1, 1, n, r, i);if (r == -1) break;int p = b[r] / i;cnt += p + 1, r += i * (p + 1);}print(cnt), putc(' ');}return 0; } 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
- 上一篇: 什么是青枝骨折
- 下一篇: 头孢和什么不能一起吃