二分
求最大/最小值 最小化最大值/最大化最小值 求第k小/大值(难)
一。最大/小值
1.牛客周赛E
一条路径上各节点的权值中最大的权值为该路径权值,求s到t所有路径中最小的权值(最小化最大值)
方法一:二分答案,若最大权值mid可以让s到t,那么r=mid-1
方法二:改造dj最短路算法
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
vector<int> e[N];
int main()
{
int n,m,a,b,s,t;
cin>>n>>m>>a>>b>>s>>t;
for(int i=0;i<m;i++){
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
//求时差
auto bfs=[&](int s)->vector<int>{
queue<int> q;
q.push(s);
vector<int>vis(n+1);
vector<int>w(n+1);
vis[s]=1;
w[s]=0;
while(q.size()){
int u=q.front();q.pop();
for(auto k:e[u]){
if(vis[k])continue;
vis[k]=1;
w[k]=w[u]+1;
q.push(k);
}
}
return w;
};
vector<int>w1=bfs(a);
vector<int>w2=bfs(b);
for(int i=1;i<=n;i++)w1[i]=abs(w1[i]-w2[i]);
if(s==t){//下面的二分check不会检查能否到自身
cout<<w1[s]<<'\n';
return 0;
}
//二分检查在mid条件下s能否到t
auto check=[&](int mid)->int{
if(w1[s]>mid)return 0;
if(w1[t]>mid)return 0;
queue<int> q;
q.push(s);
vector<int> vis(n+1);
vis[s]=1;
while(q.size()){
int u=q.front();q.pop();
for(auto k:e[u]){
if(vis[k]||w1[k]>mid)continue;
vis[k]=1;
if(k==t)return 1;
q.push(k);
}
}
return 0;
};
int l=w1[s],r=1e6,mid;
while(l<=r){
mid=(l+r)/2;
if(check(mid))r=mid-1;
else l=mid+1;
}
cout<<l<<'\n';
return 0;
}
点击查看代码
import sys
from collections import deque
input = sys.stdin.readline
n, m, a, b, s, t = map(int, input().split())
g = [[] for _ in range(n + 1)]
for _ in range(m):
u, v = map(int, input().split())
g[u].append(v)
g[v].append(u)
# BFS求最短距离
def bfs(st):
dis = [-1] * (n + 1)
q = deque([st])
dis[st] = 0
while q:
u = q.popleft()
for v in g[u]:
if dis[v] == -1:
dis[v] = dis[u] + 1
q.append(v)
return dis
# 计算所有结点的时差
da = bfs(a)
db = bfs(b)
w = [abs(da[i] - db[i]) for i in range(n + 1)]
# 判断答案mid是否可行
def check(mid):
if w[s] > mid or w[t] > mid:
return False
vis = [False] * (n + 1)
q = deque([s])
vis[s] = True
while q:
u = q.popleft()
if u == t:
return True
for v in g[u]:
if not vis[v] and w[v] <= mid:
vis[v] = True
q.append(v)
return False
# 二分答案
l, r = 0, max(w)
while l < r:
mid = (l + r) // 2
if check(mid):
r = mid
else:
l = mid + 1
print(l)
二。第k大/小值
1.丑数 III
丑数是可以被 a 或 b 或 c 整除的正整数,找出第 n 个丑数。
方法:利用容斥可以求某个数内含有多少丑数,利用最小式二分求第n个。
点击查看代码
class Solution:
def nthUglyNumber(self, n: int, a: int, b: int, c: int) -> int:
#二分+容斥
l=1
r=10**18
ans=-1
def check(mid):
three_lcm=math.lcm(math.lcm(a,b),c)
sum=mid//a+mid//b+mid//c-mid//math.lcm(a,b)-mid//math.lcm(a,c)-mid//math.lcm(b,c)+mid//three_lcm
return sum>=n
while l<=r:
mid=(l+r)//2
if check(mid):
ans=mid
r=mid-1
else:
l=mid+1
return ans
2.阶乘函数后 K 个零
(1)含0数量就是质因数中2*5的个数,但2数量很多,所以就看5的个数。
(2)到5多一个0,到10多一个0...到25多两个0,到30多一个0...到125多3个0。所以一个数的阶乘末尾0的个数就是除以5+除以25+除以125...
(3)显然这是线性的,数越大,阶乘末尾含0个数只会不变或变多。所以可以二分,求阶乘含0个数<=k的最大数,再求<=k-1的最大数,利用容斥相减即=k的。
点击查看代码
class Solution:
def preimageSizeFZF(self, k: int) -> int:
if k==0:
return 5
def check(mid,x):
sum=0
while mid: # mid/5+mid/25+mid/125...
sum+=mid//5
mid//=5
return sum<=x
def f(x):
l=1
r=10**18
while l<=r:
mid=(l+r)//2
if check(mid,x):
l=mid+1
ans=mid
else:
r=mid-1
return ans
return f(k)-f(k-1)
浙公网安备 33010602011771号