while (l < r)
{
int mid = l + r >> 1; //(l+r)/2
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
while (l < r)
{
int mid = l + r + 1 >> 1;
//(l+r+1)/2,往右找答案要加1
if (check(mid)) l = mid;
else r = mid - 1;
}
分巧克力
https://www.luogu.com.cn/problem/P8647
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int h[maxn];
int w[maxn];
int n,k;
bool check(int ans)
{int sum=0;
for(int i=0;i<n;i++)
{
sum+=(h[i]/ans)*(w[i]/ans);
}
if(sum<k)return false;
else
return true;
}
int bs(int l,int r)
{
while(l<r)
{ int mid=(l+r+1)>>1;//写在while外面导致tle
if(check(mid))
l=mid;
else
r=mid-1;
}
return l;
}
int main()
{
cin>>n>>k;
for(int i=0;i<n;i++)
{
cin>>h[i]>>w[i];
}
cout<<bs(1,maxn);
}
杨辉三角(二分答案行+数学性质)
#include<cstdio>
typedef long long LL;
const LL INF=1e9;
LL n;
LL C(LL a,LL b){
LL res=1;
for(LL i=a,j=1;j<=b;i--,j++){
res=res*i/j;
if(res>n) // fixed
return res;
}
return res;
}
int main(){
scanf("%lld",&n);
// 只需遍历 16 行
if(n==1){
printf("1");
return 0;
}
for(int i=16;i>=0;i--){
LL l=2*i,r=INF,mid,lim;
while(l<=r){
mid=(l+r)>>1,lim=C(mid,i);
//第mid行,第i条对角线
if(lim==n){
printf("%lld",(mid+1)*mid/2+i+1);
//因为从0行开始,但是因为在计算元素位置时,实际索引从 1 开始
return 0;
}else if(lim<n)
l=mid+1;
else{
r=mid-1;
}
}
}
return 0;
}
#include<cstdio>
typedef long long LL;
const LL INF=1e9;
LL n;
LL C(LL a,LL b){
LL res=1;
for(LL i=a,j=1;j<=b;i--,j++){
res=res*i/j;
if(res>n) // fixed
return res;
}
return res;
}
int main(){
scanf("%lld",&n);
// 只需遍历 16 行
if(n==1){
printf("1");
return 0;
}
for(int i=16;i>=0;i--){
LL l=2*i,r=INF,mid,lim;
while(l<=r){
mid=(l+r)>>1,lim=C(mid,i);
//第mid行,第i条对角线
if(lim==n){
printf("%lld",(mid+1)*mid/2+i+1);
//因为从0行开始,但是因为在计算元素位置时,实际索引从 1 开始
return 0;
}else if(lim<n)
l=mid+1;
else{
r=mid-1;
}
}
}
return 0;
}
递增三元组
思维转化,把b看为桥梁,分别枚举a和c相乘即可
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 5;
int n , a[N] , b[N] , c[N] , ans;
signed main(){
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= n; i++) cin >> b[i];
for(int i = 1; i <= n; i++) cin >> c[i];
sort(a + 1 , a + 1 + n);
sort(c + 1 , c + 1 + n);
//排序,好进行二分
for(int j = 1; j <= n; j++){
int cnta = lower_bound(a + 1 , a + 1 + n , b[j]) - a - 1;
//大于等于,cnta不需要再+1,因为算的是个数。
int cntc = upper_bound(c + 1 , c + 1 + n , b[j]) - c;
cntc = n - cntc + 1;
//二分找出i的种类数和j的种类数
ans += cnta * cntc;//乘法原理累计答案
}
cout << ans;
return 0;
}
第k小的和
#include <cstdio>
#include <algorithm>
using namespace std;
int n, m;
long long K, l = 0, r = 2e9, mid, A[100005], B[100005];
inline bool check() {
// 判断小于等于当前 mid 的 C[i] 个数是否小于 K,小于K说明b应该更大
//判断给定的 mid 值是否能保证存在至少 K 个数对 (A[i], B[j]) 使得 A[i] + B[j] 小于等于 mid
long long cnt = 0;
for (int i = 1; i <= n; ++i) // 遍历 A 数组
cnt += upper_bound(B + 1, B + m + 1, mid - A[i]) - B - 1; // 计算小于等于 mid - A[i] 的 B[j] 个数。
//A[i]+B[i]是第k小,枚举a看几个b满足
return cnt < K;
}
int main() {
scanf("%d%d%lld", &n, &m, &K);
for (int i = 1; i <= n; ++i)
scanf("%lld", A + i);
for (int i = 1; i <= m; ++i)
scanf("%lld", B + i);
sort(B + 1, B + m + 1); // 将 B 数组排序以便二分查找
//根据数据可知不能直接暴力计算c数组
while (l < r) { // 二分答案,mid枚举第k小数的值
mid = (l + r) >> 1;
if (check())
l = mid + 1;
else
r = mid;
}
printf("%lld", l);
return 0;
}
三体攻击(三维差分前缀和+二分)
https://www.luogu.com.cn/problem/P8666
#include <bits/stdc++.h>
#define ll long long
#define mem(a, m) memset(a, m, sizeof(a));
using namespace std;
const int N=1e7+5;
int A, B, C, m;
int d[N];//血量
int b[N];//差分
struct node{
int la, ra, lb, rb, lc, rc, h;
}t[N];
int get_xyz(int x, int y, int z)
{
return ((x-1)*B+(y-1))*C+(z-1)+1;
//将三维数组转化为一维数组,题目提示了这种映射方式能保证不冲突)
}
bool check(int mid)
{
mem(b, 0);
//差分
for (int i=1; i<=mid; i++)
{
//没1
b[get_xyz(t[i].la, t[i].lb, t[i].lc)]+=t[i].h;
//一个1
b[get_xyz(t[i].ra+1, t[i].lb, t[i].lc)]-=t[i].h;
b[get_xyz(t[i].la, t[i].rb+1, t[i].lc)]-=t[i].h;
b[get_xyz(t[i].la, t[i].lb, t[i].rc+1)]-=t[i].h;
//两个1
b[get_xyz(t[i].ra+1, t[i].rb+1, t[i].lc)]+=t[i].h;
b[get_xyz(t[i].ra+1, t[i].lb, t[i].rc+1)]+=t[i].h;
b[get_xyz(t[i].la, t[i].rb+1, t[i].rc+1)]+=t[i].h;
b[get_xyz(t[i].ra+1, t[i].rb+1, t[i].rc+1)]-=t[i].h;
}
for(int i=1;i<=A;i++)
{
for(int j=1;j<=B;j++)
{
for(int k=1;k<=C;k++)
{
//前缀和
//一个1的加,两个1的减,再把三个1重复减去的加上
b[get_xyz(i, j, k)]+=
b[get_xyz(i-1, j, k)]+
b[get_xyz(i, j-1, k)]+
b[get_xyz(i, j, k-1)]-
b[get_xyz(i-1, j-1, k)]-
b[get_xyz(i-1, j, k-1)]-
b[get_xyz(i, j-1, k-1)]+
b[get_xyz(i-1, j-1, k-1)];
//三维差分模板(背就得了)
if(b[get_xyz(i, j, k)] > d[get_xyz(i, j, k)]) return 1;
}
}
}
return 0;
}
int main()
{
ios::sync_with_stdio(false);
cin >> A >> B >> C >> m;
for(int i=1;i<=A;i++)
{
for(int j=1;j<=B;j++)
{
for(int k=1;k<=C;k++)
{
cin >> d[get_xyz(i, j, k)];
}
}
}
for(int i=1;i<=m;i++)
{
cin >> t[i].la >> t[i].ra >> t[i].lb >> t[i].rb >> t[i].lc >> t[i].rc >> t[i].h;
}
int l=1, r=m;
while(l < r)
{
int mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid+1;
}
cout << l;
return 0;
}
青蛙过河(双指针/二分+贪心)
1)双指针
思维
#include <bits/stdc++.h>
#define N 100005
using namespace std;
int n,T,h[N],ans;
int main() {
//双指针滑动窗口问题
scanf("%d%d",&n,&T);T<<=1;
//2T次课
for(int i=1;i<n;++i) scanf("%d",&h[i]);
for(int i=1,j=0,sum=0;i<n;++i) {
while(j<n&&sum<T) sum+=h[++j];
ans=max(ans,j-i+1);
//因为要保证所有长度为y的区间都满足和大于T,所以取最大值
//必须满足任意一个长度为y的区间和大于T。
sum-=h[i];//窗口向右滑动,左端点出界
}
printf("%d\n",ans);
return 0;
}
2)二分
#include <bits/stdc++.h>
#define i64 long long
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define per(i, a, b) for (int i = (a); i >= (b); i--)
using namespace std;
const int N = 1e5 + 5;
int n, x;
i64 arr[N], sum[N];
bool check(int y) {
rep(i, y, n - 1) if(sum[i] - sum[i - y] < 2 * x) return false;
return true;
}
int main() {
cin >> n >> x;
rep(i, 1, n - 1) {
cin >> arr[i];
sum[i] = sum[i - 1] + arr[i];//前缀和
}
int l = 1, r = n;
while(l < r) {
int mid = (l + r) / 2;
if(check(mid)) r = mid;
else l = mid + 1;
}
cout << r << endl;
return 0;
}