P6859 蝴蝶與花 思維 + 資料結構優化
阿新 • • 發佈:2020-10-20
P6859 蝴蝶與花 思維 + 資料結構優化
題意
給定一個\(12\)串,問能否找到\(l\)最小的區間\([l,r]\)使得\(sum[l,r]\)恰好等於\(s\)
過程中可以修改單點,修改後也只能是\(1或者2\)
串的長度\(n\),\(m\)次詢問
對每個詢問若有合法方案輸出這個方案的\(l,r\)否則輸出\(none\)
\[1\leq n ,m\leq 2\times 10^6\\ 0\leq s \le 2^{31}-1 \]分析
顯然不能直接暴力
我們利用字首和維護區間和,如果以1為起點,二分出和不小於\(k\)的\(r\),容易發現而分出的區間的和要麼是\(k\),要麼是\(k+1\)
假設二分出\([l,r_1]\),\([l+1,r_2]\)且兩個區間的和都是\(k+1\)
可以發現
- \(a_l = 2\)
- \(a_{r1} = 2\)
- \(r2 = r1 + 1\)
這樣發現其實和連續\(2\)的個數有關。
規律總結後有如下性質:
二分出從位置1的開始和不小於\(k\)的右端點\(p\)。利用資料結構求出位置1和位置\(p\)後連續\(2\)的個數分別為\(cnt1,cnt2\)
- \(cnt1 < cnt2\) 時,區間\([2 + cn1,p + cnt1]\)就是答案
- \(cnt1 \geq cnt2\)時,區間\([1 + cnt2,p+cnt2]\)
只需要解決兩個問題
- 如何找到第一個字首和不小於\(k\)的位置
- 如何求出一個位置後面有多少個連續的2
這兩個問題都可以在樹狀陣列內2分實現(注意如果用2分 + 樹狀陣列 複雜度會多一個log)
對於問題2,只需要判斷區間\([p,p + len - 1]\)的和是否是\(2 \times len\)即可
當然也可以用線段樹複雜度\(O(mlogn)\)
程式碼
ll c[maxn]; struct BIT { int n; void add(int x, int y) { for (int i = x; i < maxn; i += i & -i) { c[i] += y; } } int query(int x) { ll res = 0; for (int i = x;i; i -= i & -i) { res += c[i]; } return res; } int query1(int k) { int p = 0, sum = 0; for (int i = 20; i >= 0; i--) { int s = (1 << i); if (sum + c[s + p] <= k) p += s, sum += c[p]; } return p; } int query2(int k) { int p = 0, sum = 0; int tmp = query(k - 1); for (int i = 20; i >= 0; i--) { int s = (1 << i); if ((sum + c[p + s] - tmp == (p + s - k + 1) * 2) || (p + s < k)) p += s, sum += c[p]; } return p; } }; int a[maxn]; int main() { int n = readint(); int m = readint(); BIT bit; bit.n = n; for (int i = 1; i <= n; i++) a[i] = readint(), bit.add(i, a[i]); bit.add(n + 1, 1e9); char op[5]; while (m--) { scanf("%s", op); if (op[0] == 'C') { int x = readint(); int y = readint(); bit.add(x, y - a[x]); a[x] = y; } else { int x = readint(); int pos = bit.query1(x); if (!x || x > bit.query(n)) puts("none"); else if (bit.query(pos) == x) printf("%d %d\n", 1, pos); else { pos++; int len1 = bit.query2(1), len2 = bit.query2(pos) - pos + 1; if (len1 < len2) { if (pos + len1 <= n) printf("%d %d\n", 2 + len1, pos + len1); else puts("none"); } else { if (pos + len2 <= n) printf("%d %d\n", 1 + len2, pos + len2); else puts("none"); } } } } }