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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

【线段树】二进制(luogu 4428)

發(fā)布時(shí)間:2023/12/3 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【线段树】二进制(luogu 4428) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

正題

luogu 4428


題目大意

給你一個(gè)01串,讓你進(jìn)行一下兩種操作:
1.將其中一位取反
2.問你某一段中有多少個(gè)子串滿足有一種排列方案,使得組成的二進(jìn)制數(shù)是3的倍數(shù)


解題思路

不難發(fā)現(xiàn),因?yàn)?%3=2,所以2的冪%3的結(jié)果按121212…的規(guī)律循環(huán)

如果一個(gè)子串中1的個(gè)數(shù)為偶數(shù)個(gè),可以讓它們?cè)谙噜徫?#xff0c;這樣就可以被三整除

如果一個(gè)子串中1的個(gè)數(shù)為奇數(shù)個(gè),那么要先拿出三個(gè)1,分開放,組成10101,這樣才能讓其被3整除

\\

那么就有了以下兩種情況:

1.1的個(gè)數(shù)為偶數(shù)
2.1的個(gè)數(shù)為奇數(shù),且num1>1,num0?2num_1>1,num_0\geqslant 2num1?>1,num0??2

如果計(jì)算這兩種情況,會(huì)十分困難

那么考慮用合法方案數(shù)=總方案數(shù)-不合法方案數(shù)

不合法的用以下情況

1.1的個(gè)數(shù)為奇數(shù),且0的個(gè)數(shù)小于2
2.只有1個(gè)1

\\
以上兩種情況可以用線段樹計(jì)算

每個(gè)位置維護(hù)以下信息:

1.ld/rd0/1,0/1ld/rd_{0/1,0/1}ld/rd0/1,0/1?強(qiáng)行經(jīng)過左/右端點(diǎn)且0的出現(xiàn)次數(shù)為0/1,1的出現(xiàn)次數(shù)為偶數(shù)/奇數(shù)
2.lo/ro0/1/2lo/ro_{0/1/2}lo/ro0/1/2?,強(qiáng)行經(jīng)過左/右端點(diǎn)且恰好出現(xiàn)1次,0的出現(xiàn)次數(shù)為0/1/大于2
3.s0,s1,l0,r0,0的個(gè)數(shù),1的個(gè)數(shù),以左端點(diǎn)為起點(diǎn)0的個(gè)數(shù),以右端點(diǎn)為終點(diǎn)0的個(gè)數(shù)

然后計(jì)算不合法的,把01和10這兩種不合法的情況減掉


代碼

#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ls x*2 #define rs x*2+1 #define N 100100 #define ll long long using namespace std; ll n, m, x, y, a[N]; struct node {ll ld[2][2], rd[2][2], lo[3], ro[3], s0, l0, r0, s1, s;void clean(){memset(ld, 0, sizeof(ld));memset(rd, 0, sizeof(rd));memset(lo, 0, sizeof(lo));memset(ro, 0, sizeof(ro));s1 = s0 = l0 = r0 = s = 0;}void add(ll x){clean();if (x) ld[0][1] = rd[0][1] = lo[0] = ro[0] = s1 = s = 1;else ld[1][0] = rd[1][0] = s0 = l0 = r0 = 1;return;} }T[N<<2]; node merge(node a, node b) {node c;c.clean();for (ll i = 0; i <= 1; ++i)for (ll j = 0; j <= 1; ++j){c.ld[i][j] += a.ld[i][j];//計(jì)算ld/rdc.rd[i][j] += b.rd[i][j];if (i >= a.s0) c.ld[i][j] += b.ld[i - a.s0][j^(a.s1&1)];//左邊的0不夠,右邊的也要算進(jìn)去if (i >= b.s0) c.rd[i][j] += a.rd[i - b.s0][j^(b.s1&1)];}for (ll i = 0; i <= 2; ++i){c.lo[i] += a.lo[i];//計(jì)算lo/roc.ro[i] += b.ro[i];if (!a.s1) c.lo[min(2ll, i + a.s0)] += b.lo[i];//左邊沒有1,右邊的也要算進(jìn)去if (!b.s1) c.ro[min(2ll, i + b.s0)] += a.ro[i];}if (a.s1 == 1 && b.l0)//左邊只有1個(gè)1,右邊的0也要算進(jìn)去{if (!a.s0) c.lo[1]++, c.lo[2] += b.l0 - 1;//10的情況只有1個(gè)0,其他都有2個(gè)以上的0else c.lo[2] += b.l0;}if (b.s1 == 1 && a.r0){if (!b.s0) c.ro[1]++, c.ro[2] += a.r0 - 1;else c.ro[2] += a.r0;}c.s0 = a.s0 + b.s0;c.s1 = a.s1 + b.s1;c.l0 = a.s1 ? a.l0 : a.l0 + b.l0;//左邊全是0,右邊的也要算進(jìn)去c.r0 = b.s1 ? b.r0 : b.r0 + a.r0;c.s = a.s + b.s;//子區(qū)間的c.s += a.rd[0][0] * (b.ld[0][1] + b.ld[1][1]);//奇數(shù)個(gè)1,且0的個(gè)數(shù)小于2c.s += a.rd[0][1] * (b.ld[0][0] + b.ld[1][0]);c.s += a.rd[1][0] * b.ld[0][1];c.s += a.rd[1][1] * b.ld[0][0];if (a.r0) c.s += a.r0 * (b.lo[0] + b.lo[1] + b.lo[2]) - b.lo[0];//只有1個(gè)1的,要減去0+1的情況,因?yàn)?的個(gè)數(shù)小于2,前面有計(jì)算過if (b.l0) c.s += b.l0 * (a.ro[0] + a.ro[1] + a.ro[2]) - a.ro[0];return c; } void build(ll x, ll l, ll r)//線段樹 {if (l == r){T[x].add(a[l]);return;}ll mid = l + r >> 1;build(ls, l, mid);build(rs, mid + 1, r);T[x] = merge(T[ls], T[rs]);return; } void change(ll x, ll l, ll r, ll y, ll z) {if (l == r){T[x].add(z);return;}ll mid = l + r >> 1;if (y <= mid) change(ls, l, mid, y, z);else change(rs, mid + 1, r, y, z);T[x] = merge(T[ls], T[rs]);return; } node ask(ll x, ll L, ll R, ll l, ll r) {if (L == l && R == r) return T[x];ll mid = L + R >> 1;if (r <= mid) return ask(ls, L, mid, l, r);else if (l > mid) return ask(rs, mid + 1, R, l, r);else return merge(ask(ls, L, mid, l, mid), ask(rs, mid + 1, R, mid + 1, r)); } int main() {scanf("%lld", &n);for (ll i = 1; i <= n; ++i)scanf("%lld", &a[i]);build(1, 1, n);scanf("%lld", &m);while(m--){scanf("%lld", &x);if (x == 1){scanf("%lld", &x);a[x] ^= 1;change(1, 1, n, x, a[x]);}else{scanf("%lld%lld", &x, &y);printf("%lld\n", (y - x + 1) * (y - x + 2) / 2 - ask(1, 1, n, x, y).s);//總方案數(shù)減去不合法方案數(shù)}}return 0; }

總結(jié)

以上是生活随笔為你收集整理的【线段树】二进制(luogu 4428)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。