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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

【算法学习】整体二分

發(fā)布時間:2023/12/10 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【算法学习】整体二分 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

我們開門見山,講講一道sb題:

給你一個數(shù)組,查這個數(shù)組的第x大元素。

排序?可以

二分?怎么做啊?

二分出一個mid,判斷這個數(shù)組中有多少個數(shù)小于等于mid,如果個數(shù)大于等于x,就遞歸到[l,mid]區(qū)間,否則是[mid+1,r]區(qū)間,這樣遞歸下去就能得到結(jié)果。

怎么計算小于等于mid的個數(shù)?

for一遍原數(shù)組?其實只要存儲數(shù)組中值在[l,r]中的數(shù),然后計算就好,而對于[1,l-1],可以預(yù)先存儲。

怎么計算?樹狀數(shù)組,范圍太大就離散化。

這樣很麻煩吧?排序更好吧?

但是當(dāng)我給你多個詢問時,你就不會這么覺得了。

回顧剛才的過程,我們做上圖的類比。

把詢問看成球,答案的區(qū)間就是這個矩形,每一次我們將上一層的球判斷它會掉到下層的哪一塊中,掉到底層就是獲得了答案。

剛剛的過程就是球只有1個的情況。

球有多個時,就是整體二分!

就是對于當(dāng)前的mid,對每一個詢問都計算答案是大了還是小了,然后決定分配到下一層的哪邊。

要注意保證時間復(fù)雜度哦!

一道例題:HDU 5412。

查詢區(qū)間K小,要支持單點修改。

看代碼:

1 #include<cstdio> 2 int n,q,a[100001],sum[300001],ans[300001],que1[300001],que2[300001],bit[300001]; 3 int I[300001],type[300001],ql[300001],qr[300001],k[300001],cnt; 4 inline void ins(int t,int l,int r,int x){type[++cnt]=t,ql[cnt]=l,qr[cnt]=r,k[cnt]=x,I[cnt]=cnt;} 5 inline void Ins(int i,int x){for(;i<=n;bit[i]+=x,i+=i&-i);} 6 inline int Qur(int i){int Sum=0;for(;i;Sum+=bit[i],i-=i&-i);return Sum;} 7 void divide(int l,int r,int low,int upp){ 8 if(l>r) return; 9 if(low==upp) {for(int i=l;i<=r;++i) if(type[I[i]]==3) ans[I[i]]=low; return;} 10 int mid=low+upp>>1,cl=0,cr=0; 11 for(int i_=l,i;i_<=r;++i_){ 12 i=I[i_]; 13 if(type[i]==1) {if(k[i]<=mid) Ins(ql[i],1), que1[++cl]=i; else que2[++cr]=i;} 14 if(type[i]==2) {if(k[i]<=mid) Ins(ql[i],-1), que1[++cl]=i; else que2[++cr]=i;} 15 if(type[i]==3){ 16 int tmp=Qur(qr[i])-Qur(ql[i]-1); 17 if(k[i]<=sum[i]+tmp) que1[++cl]=i; 18 else que2[++cr]=i, sum[i]+=tmp; 19 } 20 } 21 for(int i_=l,i;i_<=r;++i_){ 22 i=I[i_]; 23 if(type[i]==1) if(k[i]<=mid) Ins(ql[i],-1); 24 if(type[i]==2) if(k[i]<=mid) Ins(ql[i],1); 25 } 26 for(int i=1;i<=cl;++i) I[l+i-1]=que1[i]; 27 for(int i=1;i<=cr;++i) I[l+cl+i-1]=que2[i]; 28 divide(l,l+cl-1,low,mid); divide(l+cl,r,mid+1,upp); 29 } 30 int main(){ 31 scanf("%d",&n); 32 for(int i=1;i<=n;++i) scanf("%d",a+i), ins(1,i,i,a[i]); 33 scanf("%d",&q); 34 for(int i=1,t,x,y,z;i<=q;++i){ 35 scanf("%d",&t); 36 if(t==1) scanf("%d%d",&x,&y), ins(2,x,x,a[x]), ins(1,x,x,y), a[x]=y; 37 else scanf("%d%d%d",&x,&y,&z), ins(3,x,y,z); 38 } 39 divide(1,cnt,1,1000000000); 40 for(int i=1;i<=cnt;++i) if(ans[i]) printf("%d\n",ans[i]); 41 return 0; 42 } View Code

一道例題:洛谷 P3332。

查詢區(qū)間K大,要支持區(qū)間加入。

看代碼:

1 #include <cstdio> 2 3 typedef long long LL; 4 const int MN = 50005; 5 6 int N, Q; 7 8 int opt[MN], L[MN], R[MN]; LL V[MN]; 9 int P[MN]; 10 int s1[MN], s2[MN], t1, t2; 11 12 LL Sum[MN]; 13 int Ans[MN]; 14 15 LL b1[MN], b2[MN]; 16 inline void Add(LL *b, int i, LL x) { for(; i <= N; i += i & -i) b[i] += x; } 17 inline LL Qur(LL *b, int i) { LL A = 0; for(; i; i -= i & -i) A += b[i]; return A; } 18 inline void Add(int l, int r, LL x) { 19 ++r; 20 Add(b1, l, x), Add(b2, l, (l - 1) * x); 21 Add(b1, r, -x), Add(b2, r, -(r - 1) * x); 22 } 23 inline LL Qur(int l, int r) { 24 --l; 25 return r * Qur(b1, r) - Qur(b2, r) - l * Qur(b1, l) + Qur(b2, l); 26 } 27 28 void Solve(int l, int r, int lb, int rb) { 29 if (lb == rb) { 30 for (int i = l; i <= r; ++i) 31 if (opt[P[i]] == 2) 32 Ans[P[i]] = lb; 33 return ; 34 } 35 int mid = lb + rb >> 1; 36 t1 = t2 = 0; 37 for (int i = l; i <= r; ++i) { 38 if (opt[P[i]] == 1) { 39 if (V[P[i]] <= mid) 40 s1[++t1] = P[i]; 41 else { 42 Add(L[P[i]], R[P[i]], 1); 43 s2[++t2] = P[i]; 44 } 45 } 46 else { 47 LL num = Sum[P[i]] + Qur(L[P[i]], R[P[i]]); 48 if (num >= V[P[i]]) 49 s2[++t2] = P[i]; 50 else { 51 Sum[P[i]] = num; 52 s1[++t1] = P[i]; 53 } 54 } 55 } 56 for (int i = l; i <= r; ++i) { 57 if (opt[P[i]] == 2) continue; 58 if (V[P[i]] > mid) 59 Add(L[P[i]], R[P[i]], -1); 60 } 61 int pos = l; 62 for (int i = 1; i <= t1; ++i) 63 P[pos++] = s1[i]; 64 for (int i = 1; i <= t2; ++i) 65 P[pos++] = s2[i]; 66 int M = l + t1 - 1; 67 Solve(l, M, lb, mid); 68 Solve(M + 1, r, mid + 1, rb); 69 } 70 71 int main() { 72 scanf("%d%d", &N, &Q); 73 for (int i = 1; i <= Q; ++i) { 74 scanf("%d%d%d%lld", opt + i, L + i, R + i, V + i); 75 P[i] = i; 76 } 77 Solve(1, Q, -N, N); 78 for (int i = 1; i <= Q; ++i) { 79 if (opt[i] == 1) continue; 80 printf("%d\n", Ans[i]); 81 } 82 return 0; 83 } View Code

?

轉(zhuǎn)載于:https://www.cnblogs.com/PinkRabbit/p/8039083.html

總結(jié)

以上是生活随笔為你收集整理的【算法学习】整体二分的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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