題目位址
前置知識:線段樹
給定乙個長度為 \(n\) 的 \(01\) 串,\(m\) 次操作:
先來解決乙個子問題,什麼樣的序列重排是 \(3\) 的倍數?
設 \(s\) 為序列上 \(1\) 的個數,序列長度為 \(len\)。將二進位制下每位下的權列列舉出來,分別是 \(1, 2, 4, 8, 16, 32....\)
換成 \(\bmod 3\) 意義下的權:\(1, 2, 1, 2, 1, 2, 1...\)
可以觀察出(或者隨便證明一下 )這是乙個 \(1, 2\) 迴圈的權值。設 \(1\) 的權數量為 \(a\),\(2\) 的權數量為 \(b\)。
不難發現,當 \(len\) 為偶數時,\(a = b = \dfrac\);當 \(len\) 為奇數時,\(a = \dfrac, b = \dfrac\)
假設我們權值為 \(1\) 的位置上有 \(x\) 個是 \(1\),權值為 \(2\) 的位置上有 \(y\) 個是 \(1\),要滿足 \(x + y = s\)
充要條件就是:
\[3 | x + 2y \leftrightarrow x+2y \equiv 0 \pmod 3\leftrightarrow 3y+x-y \equiv 0 \pmod 3\leftrightarrow x-y \equiv 0 \pmod 3 \leftrightarrow x \equiv y \pmod 3
\]經過完這步轉化,我們需要做的就是構造出一組合理的 \(x, y\)。使得 \(x, y\) 能填到位置上(顯然順序已經無關)。
由於 \(x \ge y, a \ge b\),不妨讓 \(x\) 取填到 \(a\) 個位置上,\(y\) 填到 \(b\) 個位置上,即要滿足 \(0 \le x \le a, 0 \le y \le b\)
因為 \(s, a, b\) 是有限的,不難想到貪心情況下,\(x, y\) 要盡量接近,這樣容錯率更大(比如 \(x = y\) 的情況可以支援 \(s = 0\),\(x = y + 3\) 的情況勢必 \(s, x \ge 3\),\(x = y + 6\) 的情況勢必 \(s, x \ge 6\)。或者另外一種解釋,任意 \(x \equiv y \pmod 3\) 的情況都可以通過大的那一項 \(-3\),小的那一項 \(+3\),最終變為 \(x = y + 3\) 或者是 \(x = y\) 的形式,不妨手玩一下)
我們考慮 \(s\) 是偶數的情況下,顯然取 \(x = y\),即保持兩個權貢獻一樣的拿,那麼肯定是可以滿足的。
我們考慮 \(s\) 是奇數的情況下,因為若 \(x = y\),那麼 \(x + y = s\) 一定是奇數所以不能這麼取。所以一定得是 \(x = y + 3\) 的形式,這種情況下 \(x = \dfrac, y = \dfrac\),在這種形式下, 取帶入剛才的 \(x, y\) 需要滿足的區間式,討論 \(s\) 在何時合法:
至此,我們發現乙個序列是否合法取決於它的長度 \(len\) 奇偶性以及 \(1\) 的數量 \(s\)。
顯然,符合要求的 \(s\) 非常多,不好統計,也不好優化,反過來,考慮不合法數量:
然後發現後面兩個條件可以合併,讓合法性與 \(len\) 無關,並且 \(s = 1\) 和後面兩個條件有一部分重疊,即 \(s = 1\) 且 \(0\) 的個數 \(\le 1\) 時,所以再優化一下:
然後用總的方案減去不合法方案就可以求出合法方案了~
現在,問題變成了支援單點修改,然後 \(o(1)\) 或者 \(o(\log)\) 的時間在 \([l, r]\) 查詢滿足條件(上一步總結的)的連續子串行個數。
咋做咋做?我也不會
滿足條件的連續子串行,可以看做是列舉左右端點,或者是二元計數。想想我們之前學過的演算法啥能計算二元計數?相信你想到了,就是歸併排序,他求解逆序對的原理就是說合併兩個區間,然後把兩個區間內各自選乙個數,然後把貢獻記錄到答案上。
然後這題還要支援修改,通過看題解我們就非常直接的想到線段樹,然後每次合併兩個線段的時候,答案 \(=\) 左邊線段的答案 \(+\) 右邊線段的答案 \(+\) 左端點在左邊這條線段、右端點在右邊這條線段的產生的合法連續子串行。
那麼我們只要支援用常數時間複雜度計算合併兩個線段對答案的貢獻就行了!
想象一下,你現在有了兩個線段 \(a:[l, mid]\) 和 \(b:[mid + 1, r]\),合併後新的線段為 \(c\),你如何算出跨越兩條線段產生的答案貢獻?換句話說你需要維護哪些資訊?比較顯然的是,跨越顯然過 \(mid\) 和 \(mid + 1\)。即這樣的線段一定是 \(a\) 的乙個字尾和 \(b\) 的乙個字首構成的,所以資訊一定要設立字首字尾的資訊。
對於第一種條件咋搞?
不妨把那個 \(0\) 的個數 \(\ge 2\) 這個坑爹的條件去掉,僅考慮計算 \(s = 1\) 的貢獻,後面減掉就行。
首先你需要分類討論那個 \(1\) 所在的位置,不妨設他在右邊這條線段上,那麼你需要記錄 \([l, mid]\) 的字尾 \(0\) 個數,以及 \(b\) 線段的字首滿足只有乙個 \(1\) 的個數,然後兩者的乘積就是答案。
然後再左邊的話是對稱的,就不贅述了。
綜上你需要維護:
每條線段的字首 \(0\) 個數 \(l_0\) 以及 字尾 \(0\) 個數 \(r_0\)
字首滿足只有乙個 \(1\) 的個數 \(l_1\),字尾滿足只有乙個 \(1\) 的個數 \(r_1\)
\(l_1, r_1\)具體維護有很多細節,要討論全(即字尾是原來的字尾,或者成為跨域兩個的新字尾),還要記錄一段的 \(0, 1\) 總數 \(c_0, c_1\)。 \(r_1\) 為例:
第二個條件
比較顯然的是我們只關注 \(1\) 的數量的奇偶性,以及 \(0\) 的數量(而且只能為 \(0/1\))。
設 \(r_\) 為線段字尾中滿足 \(0\) 的數量為 \(i\),\(1\) 的數量 \(\bmod 2 = j\) 的數量。\(l\) 統計字首,對稱地是類似的。
那麼你列舉 \(a.r_, b.l_\) 考慮能否產生貢獻:
維護這倆破玩意挺噁心的,以 \(r\) 為例,要考慮跨域整個 \(b\),左端點到 \(a\) 的新字尾。:
重複計算了咋辦?
分類討論一下:
重複計算的 \(s = 1\),\(0\) 的個數為 \(0\) 的情況,即只有乙個 \('1'\) 的形式。這個好辦,不會再合併的時候統計(因為合併長度至少 \(\ge 2\),直接一開始的時候統計即可)。
重複計算的 \(s = 1\),\(0\) 的個數為 \(1\) 的情況。這種情況應該就是 \(mid, mid + 1\) 兩個位置成為 \(01\) 或者 \(10\) 的情況,特判一下減掉就行。
然後我們就解決了重複計算的麻煩。
\(o(n\log_2n)\)
對於這種及其毒瘤的東西可以自己寫個結構體啥的,這樣把**分治,這樣不會寫的很亂。
我相信這個碼風一點都不毒瘤。。。。
#include #include #include using namespace std;
typedef long long ll;
const int n = 100005;
int n, m, w[n];
// 線段結構體
struct seg
seg(){}
// 線段長度為 1,這個元素是 x 的時候的初始化
seg(int x)
// 合併兩個區間,同時對 ans 產生貢獻, mid 是線段 a 的右端點
seg(seg a, seg b, int mid)
} res = a.res + b.res;
// 條件 1
res += (ll)a.r0 * b.l1 + (ll)a.r1 * b.l0;
// 條件 2
res += (ll)a.r[0][0] * (b.l[0][1] + b.l[1][1]) + (ll)a.r[0][1] * (b.l[0][0] + b.l[1][0]);
res += (ll)a.r[1][0] * b.l[0][1] + (ll)a.r[1][1] * b.l[0][0];
// 減掉重複統計
if (w[mid] + w[mid + 1] == 1) res--;
} } v[n << 2];
void build(int p, int l, int r)
int mid = (l + r) >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
v[p] = seg(v[p << 1], v[p << 1 | 1], mid);
}void change(int p, int l, int r, int x)
int mid = (l + r) >> 1;
if (x <= mid) change(p << 1, l, mid, x);
else change(p << 1 | 1, mid + 1, r, x);
v[p] = seg(v[p << 1], v[p << 1 | 1], mid);
}seg query(int p, int l, int r, int x, int y)
int main() else
} return 0;
}
BJOI2018 二進位制
題目鏈結 pupil 發現對於乙個十進位制數,無論怎麼將其的數字重新排列,均不影響其是不是 的倍數。他想研究對於二進位制,是否也有類似的性質。於是他生成了乙個長為n 的二進位制串,希望你對於這個二進位制串的乙個子區間,能求出其有多少位置不同的連續子串,滿足在重新排列後 可包含前導0 是乙個3 的倍數...
BJOI2018 二進位制
題目鏈結 pupil 發現對於乙個十進位制數,無論怎麼將其的數字重新排列,均不影響其是不是 的倍數。他想研究對於二進位制,是否也有類似的性質。於是他生成了乙個長為n 的二進位制串,希望你對於這個二進位制串的乙個子區間,能求出其有多少位置不同的連續子串,滿足在重新排列後 可包含前導0 是乙個3 的倍數...
bzoj 5294 Bjoi2018 二進位制
pupil 發現對於乙個十進位制數,無論怎麼將其的數字重新排列,均不影響其是不是333 的倍數。他想研究對於二進 制,是否也有類似的性質。於是他生成了乙個長為n 的二進位制串,希望你對於這個二進位制串的乙個子區間,能求出 其有多少位置不同的連續子串,滿足在重新排列後 可包含前導0 是乙個3 的倍數。...