//2023年12月有更新。
對於一些問題,它的解滿足單調性,即如果x滿足條件,則對於任意的 i ( 1<=i<=x) 或 (x <=i <=n) (假設1和n是答案的上下界)都會滿足條件。一般遇上這種問題,我們就可以用二分答案來加快解決。這種問題常常有關鍵語句:使最大......最小、……至少是多少、求出最少的……。
對於上面的問題,在沒學二分答案的時候,我們是這麼寫的:(假設答案是上界)
for(int i=1;i<=n;++i)if(!check(i))
列舉答案,進行檢查。出現第乙個不合法的解,答案就是它前面那個值。但這樣看來,未免太慢。我們花了很長時間來檢查每乙個答案。如果答案是10000,我們就會做很多無用功。或者說我們列舉1000是正確的,那麼前999個都可以看成白列舉了,很浪費。
那麼我們該如何盡可能的少做這些無用功呢?
我們來設想一下。同樣假設答案是上界,如果我們check了10000,發現它是滿足解的,那麼答案肯定不小於10000。如果我們又check了 20000,發現它是滿足解的,那麼10000~20000內的數我們都不用列舉。又或者20000是不滿足解的,那麼答案就在10000~20000的 左閉右開區間內。這個時候我們如果」恰當地「check 15000,答案的範圍會進一步縮小。
看到這裡我們大概都會想到分塊或者二分了。一步一步地縮小答案範圍最終出解。
int l=1,r=n,ans=1;while(l<=r)
printf("%d
",ans);
是啊,zz的板子誰都會。重點就在check函式上面。我們要使用時間複雜度優秀的check來寫。怎麼寫出最好的方法呢?分析題目,優化演算法,然後大膽猜想不用證明。多做題目,積累經驗(這是最吼的!)。然後因為我走歪了,check一向非主流,但莫名好用啊(有些題目的check只要有不合法就可以立即彈出,複雜度可以降到玄學級)一般二分答案複雜度是穩定的log(r-l+1),總複雜度是o(log*f(check))。
下面分享一些題目,都是noip裡面的水題,都是可以一遍ac的。
1.noip2015 河中跳房子
最大距離最小,一看就知道是二分答案。
那麼該如何寫check呢?
我們可以check當最大距離為mid時,所需要搬走的石頭的個數。方法就是乙個小小的貪心——能不拿走就不拿走,能少拿走就少拿走,然後一次check在o(n)的時間內跑過。總時間複雜度是o(nlogn)。
#include #include#include
#include
int l,m,n,pla[50100
];int
gi()
int check(int
x) sta=pla[i];
}return
ans;
}using
namespace
std;
intmain()
printf("%d
",r);
}
我們check的返回值就是最少需要搬走的石頭個數,可以證明ans就是最優值。
2.noip2012 借教室
因為答案具有單調性——答案之前的肯定都能借到。所以我們來二分答案。
那麼該如何寫check函式呢?
對於這道題,我們需要幹的事有:區間增加和單點求值。可能大犇都想到了線段樹,但這種複雜度賊高的演算法最高只能過90分。我們沒必要用高階演算法,可以用一種小技巧處理——差分+字首和。
所 謂差分,就是把區間的操作轉移到對區間端點的操作。比如我們對乙個都是0的陣列,要在5~13這段區間加上3,只要在5這個點上加上3,在14這個點上減 掉3,這樣在算字首和的時候,0~4都是0,5~13都是3,14及以後又都成了0。這種操作修改是o(1),查詢是o(n),但你可以一遍查詢求出所有 的點是否合法。所以查詢的總複雜度就是o(n+m),題目的複雜度就是o((n+m)logm)。
#include #include#include
using
namespace
std;
int n,m,num[1010000],d[1010000],sta[1010000],end[1010000],q[1010000
],l,r;
intgi()
bool check(int
x)
return
true;}
intmain()
if(l>m)printf("0"
);
else printf("
-1\n%d
",l);
return0;
}
我們check函式返回的是當前訂單能否滿足。
3.noip2011 聰明的質檢員
一看就知道是二分答案:答案具有單調性,影象趨勢類似於乙個二次函式曲線,我們只要求出這個函式的頂點最近的整數就好了。二分答案,記錄當前答案,比較一下就好了。
這裡要解釋一下那個式子的意思:l到r內所有滿足wj>w的j的個數乘以它們的體積和。這個可以用字首和維護。開兩個字首和維護一下個數和體積和就好了。
#include #include#include
#include
#define ll long long intll n,m,s,l[
201000],r[201000],v[201000],w[201000],qnum[200100],qy[200100
],maxr;
ll gi()
ll max(ll x,ll y)
ll min(ll x,ll y)
ll check(ll x)
for(int i=1;i<=m;++i)
return ans-s;
}int
main()
printf(
"%lld\n
",k);
}return0;
}
check函式返回的是當前答案下的w的原值(不能帶abs())
如果小於0,證明這個點在原點右邊,要往左邊挪。否則往右邊挪。
那就這麼草率的結尾吧!
二分答案和三分入門
首先.我是乙個很菜很菜的萌新,所以這篇文章寫得很詳細,有很多我自己的口水話方便我理解,請各位謹慎食用qwq 以前在網上找過很多介紹二分的部落格,但都感覺對萌新不太友好,反正我當時連跳石頭都沒看懂,所以決定自己寫一篇!其中有我的想法,也借鑑了書裡的很多內容,感謝lyd。二分答案,顧名思義,就是對我們所...
二分查詢與二分答案
主要用於在乙個單調的函式中查詢某值 連續函式的情況 若當前查詢的區間是 l,r 查詢的值是 y 函式單增 設 mid l r 2 若 f mid y 則 l mid,否則 r mid 直至 r l eps 當前查詢的區間是 l,r 查詢的值是 y 函式單增 設 mid l r 2 若 f mid y...
二分查詢和二分答案
1.解釋 優點 查詢速度快。缺點 待查表為有序表。4.時間複雜度 o log n 5.示例 p2249查詢 include include using namespace std long long n,m,a 1000005 b 100005 l,r,mid,cnt,x intmain for i...