0. 开门见山
二分六连
1. 传纸条
给定一个字符串 \(a\),求一个整数 \(x\) 使得对于任意一个长度大于 \(x\) 的串 \(b\) 在 \(a\) 中出现次数至多1次。
二分+匹配。我用的是hash。
核心部分:判断 \(x\) 是否合法。
bool Check(reg int x){
int tmp=0,m=1;
for(reg int i=1;i<x;i++)
m=m*27%Mod;
for(reg int i=1;i<x;i++)
tmp=(tmp*27+(A[i]-'a'+1))%Mod;
for(reg int i=x;i<=len;i++){
tmp=(tmp*27+(A[i]-'a'+1))%Mod;
if(B[tmp])return 0;
B[tmp]=1;
tmp=((tmp-(A[i-x+1]-'a'+1)*m)%Mod+Mod)%Mod;
}
return 1;
}
2. [NOIP福建夏令营]数列分段
对于给定的一个长度为 \(N\) 的正整数数列 \(A\) ,现要将其分成 \(M(M\le N)\) 段,并要求每段连续,且每段和的最大值最小。
二分+贪心。
3. 商较小时的高精度除法
如果不想写高精度除法的话,可以二分商,然后只需要写高精度乘法即可。
4. TMZJZ8
给定总区域长度 \(n\),以及要求的单个区间长度 \(k\)(任何区间不可以重叠、边缘不可相交),共有 \(q\) 次询问,\(q_i\) 表示该点不被任何区间覆盖,求最少询问次数使得区间个数小于规定的区间个数 \(m\)。(转自本校学长题解)
二分答案。
核心部分:判断答案是否合法。
bool vis[200005];
bool Check(int u){
for(reg int i=1;i<=Q;i++)
if(i>u)vis[A[i]]=0;
else vis[A[i]]=1;
int cnt=0,len=0;bool nei=0;
for(reg int i=1;i<=N;i++){
if(!vis[i]&&!nei)len++;
else{
if(nei)nei=0;
len=0;
}
if(len==K){
len=0;
cnt++;
nei=1;
}
}
if(cnt<M)return 1;
else return 0;
}
5. [2016NOIP福建夏令营]矩阵
给定一个 \(N\) 行 \(M\) 列的非负整数矩阵,求一个最大的正方形子矩阵,该矩阵满足:
矩阵中每一个元素权值都大于0;
在满足上述条件的前提下,矩阵面积最大;
在满足上述条件的前提下,选择元素和最小的。
二分正方形边长,处理前缀和。
可以另存一个01矩阵来存每一个数是(1)否(0)大于0。然后计算的时候判断选择的区间所有数加起来是否为对应的完全平方数。
我的方法是把0设为无穷大。
6. 倒水监事
求 \(1<=i\dots j<=n\) 使得 \(a[j]>a[i]\) 并且 \(a[j]%a[i]\) 最大,输出最大值。
这题……
不需要用二分也可以做。(Orz nealchen)
#include<cstdio>
#define reg register
#define cmax(_,__) ((_)<(__)?(_)=(__),1:0)
using namespace std;
int N,M,A[200005],B[2000005],res;
int main(){
scanf("%d",&N);
for(reg int i=1;i<=N;i++){
scanf("%d",&A[i]);
cmax(M,A[i]);
B[A[i]]=A[i];
}
for(reg int i=1;i<=M*2;i++)
cmax(B[i],B[i-1]);
for(reg int i=1;i<=N;i++)
for(reg int j=A[i];j<=M;j+=A[i])
cmax(res,B[j+A[i]-1]%A[i]);
printf("%d\n",res);
return 0;
}
7. 总结
接下来还要做些稍难的二分题。多尝试一些新的套路。