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)二分

image

#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;
}
posted on 2024-09-07 20:32  Hoshino1  阅读(7)  评论(0编辑  收藏  举报