日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

CF1398F

發布時間:2023/12/3 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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?ijjj開始,每次找一個最小的j′j'j,滿足j′≥j+ij'\geq j+ijj+iaj′≥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; } 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的CF1398F的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。