1 前缀和

1 k倍区间)

https://www.luogu.com.cn/problem/P8649

之差是k的倍数,性质:两个数取余k相同时,之差是k的倍数。所以只需要记录每个前缀和取模k,每新增一个曾经出现的余数,它能和之前存入的m个相同余数组成k差,ans+=m。

并且要注意0的位置。这相当于有的不用减第一个数就是k倍,关键在于想到i,j区间都可以加上前面的部分,这样好维护

2 抓娃娃)

https://www.luogu.com.cn/problem/P9426

关键在于题目有一个隐含条件,最小区间大于最大线段。所以有一个转化:包含中点就一定包含线段。现在就是记录中点,然后前缀和求每个区间中点个数。

开小了1e5+10会全部re,要注意数据范围,还需要longlong

后面的也需要维护,不是只维护到maxx,int范围2e9,数组5e8,longlong 9e18,数组2e8

点击查看代码
#include<bits/stdc++.h>
#define int long long
const int maxn=2e6+10;
using namespace std;
int l,r;
int n,m,s[maxn],a[maxn],b[maxn],ans=0;
signed main()
{
   cin>>n>>m;
   for(int i=1;i<=n;i++)
   {
        cin>>l>>r;
    a[l+r]++;

   }
      for(int i=1;i<=maxn;i++)
   {

a[i]+=a[i-1];
   }
    for(int i=1;i<=m;i++)
   {

        cin>>l>>r;
   l*=2,r*=2;
   cout<<a[r]-a[l-1]<<endl;


   }



    return 0;
}

3 挖矿)

https://www.luogu.com.cn/problem/P10904

更新j出错
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 2e6 + 10;
int n, m, ans;
int a[maxn],b[maxn];
signed main()
{
	cin >> n >> m;
	for (int i = 1;i <= n;i++)
	{
		int x;
		cin >> x;
		if (x < 0)
			b[-x]++;
		else
			a[x]++;
	}
	for (int i = 1;i < maxn;i++)
	{
		a[i] += a[i - 1];
		b[i] += b[i - 1];
	}
	for (int i = 0;i <= m;i++)
	{
		//枚举左端点
		int t = a[i];
		if (m - 2 * i > 0)
			t += b[m - 2 * i];
		//j = 0;
		//while (2 * j + i <m)
		//{
		//	//例如,假设i是0,m是5,那么2j+0 <=5,最大的j是2(因为2*2=4<=5,而j=3的话2*3=6>5),所以这样更新不对
		//	j++;
		//	
		//}

		ans = max(ans,t);
	}
	for (int i = 0;i <= m;i++)
	{
		int t = b[i];
		if (m - 2 * i > 0)
			t += a[m - 2 * i];
		ans = max(ans, t);
		
	}

	cout << ans << endl;
}
点击查看代码
#include <bits/stdc++.h>
using namespace std;

// 定义数组的最大长度
const int N = 2e6 + 10;
// n 表示矿洞数量,m 表示最大移动距离
// l 数组用于记录负坐标矿洞的数量,r 数组用于记录正坐标矿洞的数量
// ans 用于记录最大矿石数量,cnt 用于记录原点矿洞的数量
int n, m, l[N], r[N], ans, cnt;

int main() {
    // 读取矿洞数量 n 和最大移动距离 m
    scanf("%d%d", &n, &m);
    // 遍历每个矿洞
    for (int i = 1, x; i <= n; i++) {
        // 读取当前矿洞的坐标
        scanf("%d", &x);
        if (x < 0) {
            // 如果坐标为负,将对应负坐标的矿洞数量加 1
            l[-x]++;
        } else if (x > 0) {
            // 如果坐标为正,将对应正坐标的矿洞数量加 1
            r[x]++;
        } else {
            // 如果坐标为 0,原点矿洞数量加 1
            cnt++;
        }
    }
    // 计算负坐标矿洞数量的前缀和
    for (int i = 1; i <= m; i++) {
        l[i] += l[i - 1];
        // 计算正坐标矿洞数量的前缀和
        r[i] += r[i - 1];
    }
    // 枚举所有可能的移动方案
    for (int i = 1, t; i <= m; i++) {
        // 方案一:先向左移动 i 个单位,再向右移动
        t = l[i];
        if (m - i * 2 > 0) {
            // 如果剩余移动距离大于 0,加上向右能挖到的矿洞数量
            t += r[m - i * 2];
        }
        // 更新最大矿石数量
        ans = max(ans, t);
        // 方案二:先向右移动 i 个单位,再向左移动
        t = r[i];
        if (m - i * 2 > 0) {
            // 如果剩余移动距离大于 0,加上向左能挖到的矿洞数量
            t += l[m - i * 2];
        }
        // 更新最大矿石数量
        ans = max(ans, t);
    }
    // 输出最大矿石数量加上原点矿洞数量
    printf("%d\n", ans + cnt);
    return 0;
}

2 二分

!!!!有时需要有效性检查,因为可能所有元素都大于小于,而不满足小于等于大于等于

向左找
int bin1(int l,int r)
{
    while(l<r)
    {
        
        int mid=(l+r)>>1;
    if(check(mid)) r=mid;
        else l=mid+1;
       
    }
    
     return l;
}
向右找
int bin2(int l,int r)
{
    while(l<r)
    {
        
        int mid=(l+r+1)>>1;
        if(check(mid))
            l=mid;
        else
            r=mid-1;
    }
    return l;
    
}

1 立定跳远)

https://www.luogu.com.cn/problem/P10909

注意两个整数直接相除会直接取整,所以需要先变为浮点数。再进行向上取整ceil或向下取整floor。并且使用全局变量记得有时需要重置为0;

点击查看代码
#include<bits/stdc++.h>
#define int long long
const int maxn = 2e6 + 10;
using namespace std;
int l, r;
int maxx = -1;

int n, m, a[maxn], b[maxn], ans = 0;

int check(int s)
{
	int flag = 0;
	//忘记重置全局变量
	int cou = 0;
	for (int i = 1; i <= n; i++)
	{
		int c = a[i] - a[i - 1];
		if (c > s)
		{
			if (c <= 2 * s && flag == 0)
				flag = 1;
			else
			{
				cou += ceil(c *1.0/ s) - 1;
				//当两个int整数相除时,直接舍去小数部分
				//这里非常容易出错,忘写ceil,并且cou只加了1
				//两个整数直接相除会取整,那向上取整就不管用了
				if (flag == 0)
				{
					flag = 1;
					//忘记改变
					cou--;
				}
			}


			if (cou > m)
				return 0;

		}


	}
	return 1;


}
int bin1(int l, int r)
{
	while (l < r)
	{

		int mid = (l + r) >> 1;
		if (check(mid)) r = mid;
		else l = mid + 1;

	}

	return l;
}

signed main()
{
	cin >> n >> m;


	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		maxx = max(maxx, a[i]);
	}
	cout << bin1(1, maxx);



}



2 杨辉三角形)

https://www.luogu.com.cn/problem/P8749

为了保证二分的单调性并且结合杨辉三角性质,看为第i行第j个对角线。那这个数就是C(i,j),找到i,j就可以确定这是第几个数

点击查看代码
#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<bits/stdc++.h>
//#define int long long
const int maxn = 2e6 + 10;
using namespace std;
int l, r;
int i, j;
int maxx = -1;

int n, m, a[1000][1000], b[maxn], ans = 0;

int check(int s)
{
	


	


}
int bin1(int l, int r)
{
	while (l < r)
	{

		int mid = (l + r) >> 1;
		if (check(mid)) r = mid;
		else l = mid + 1;

	}

	return l;
}

signed main()
{
	cin >> n;

	a[1] = a[2] = 1;
	int x = 1;
	for (i = 1;i < 1000;i++)
	{
		for (j = 1;j <=i;j++)
		{
			if (i == 1 || i == j)
			{
				a[i][j] = 1;
			}
			else {
				a[i][j] = a[i - 1][j] + a[i - 1][j - 1];
			}
			if(j<=ceil(i/2.0))
			b[x++] = a[i][j];
		}
	}
	if (n == 1)
		cout << 1 << endl;
	else
int ans= bin1(1, 1e9+10);
	




}

3 递增三元组)

https://www.luogu.com.cn/problem/P8667

lower_bound 不小于 upper_bound大于

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 10;
int n, m, ans,maxx,flag;
int a[maxn],b[maxn],c[maxn];
/*
int bin(int l, int r)
{
	while (l < r)
	{
		int mid = (l + r) / 2;
		if (check(mid)) r = mid;
		else l = mid + 1;

	}
	return l;
}
*/

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(b + 1, b + 1 + n);
	sort(c + 1, c + 1 + n);
	//排序,好进行二分
	for(int i = 1;i <= n;i++)
	{
		int ans1 = lower_bound(a + 1, a + 1 + n, b[i]) - (a + 1)-1+1;

		int ans2 = c+n+1-upper_bound(c + 1, c + 1 + n, b[i]);
		//二分找出i的种类数和j的种类数
		//upper大于,lower大于等于
		//c+n+1其实是最后一个元素的下一个位置,所以ans2不需要再+1
		ans += ans1 * ans2;

	}
	cout << ans << endl;

}
正常二分写,一直不对
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 10;
int n, m, ans,maxx,flag;
int a[maxn],b[maxn],c[maxn];

int bin1(int l, int r,int x)
{
	while (l < r)
	{
		int mid = (l + r) / 2;
		if (c[mid]>=x) r = mid;
		else l = mid + 1;

	}
	return l;
}

int bin2(int l, int r,int x)
{
	while (l < r)
	{
		int mid = (l + r+1) / 2;
		if (c[mid]<=x) l= mid;
		else r=mid-1;

	}
	return l;
}


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(b + 1, b + 1 + n);
	sort(c + 1, c + 1 + n);
	for (int i = 1;i <= n;i++)
	{
		int ans1 = bin1(1, n, b[i]);
		int ans2=n-bin2(1, n, b[i])+1;
		//为啥这里要+1呢:
		ans += ans1 * ans2;
	}
	cout << ans << endl;

}
正常二分 改不对
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 10;
int n, m, ans,maxx,flag;
int a[maxn],b[maxn],c[maxn];

int bin1(int l, int r,int x)
{
	while (l < r)
	{
		int mid = (l + r) / 2;
		if (a[mid]>=x) r = mid;
		else l = mid + 1;

	}
	return l;
}

int bin2(int l, int r,int x)
{
	while (l < r)
	{
		int mid = (l + r+1) / 2;
		if (c[mid]<=x) l= mid;
		else r=mid-1;

	}
	return l;
}


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(b + 1, b + 1 + n);
	sort(c + 1, c + 1 + n);
	for (int i = 1;i <= n;i++)
	{
		int ans1 = bin1(1, n, b[i])-1;
		int ans2=n-bin2(1, n, b[i]);
		
		ans += ans1 * ans2;
	}
	cout << ans << endl;

}

桶排序,记录每个数出现的个数,sum[i]表示小于等于i的个数

桶排序

#include<bits/stdc++.h>
using namespace std;
#define int long long

const int maxn=1e5+10;
int sum1[maxn],sum2[maxn];
int a[maxn],b[maxn],c[maxn];

signed main(){
	int n;
	scanf("%lld",&n);
	for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
	for (int i=1;i<=n;i++) scanf("%lld",&b[i]);
	for (int i=1;i<=n;i++) scanf("%lld",&c[i]);
	
	for (int i=1;i<=n;i++){
		sum1[a[i]]++;
		sum2[c[i]]++; 
	}
	
	for (int i=1;i<=(int)1e5;i++){
		sum1[i]+=sum1[i-1];
	}
	
	for (int i=(int)1e5;i>=0;i--){
		sum2[i]+=sum2[i+1];
	}
	
	int ans=0;
	
	for (int i=1;i<=n;i++){
		ans+=sum1[b[i]-1]*sum2[b[i]+1];
	}
	
	printf("%lld",ans);
	return 0;
}

4)青蛙跳石头

错误代码:1:忽略了要上k天课,所以要检查k次,并且因为是逐渐递减的,不能直接a[i]-=k.2:忽略了备份代码,a数组在第一个测试点改变,所以我们要存起来a。

正解:往返和x次课看成2x只青蛙从起点开始跳跃,如果y满足,那么任意一个长度为y的区间,石头高度和必须大于2x

我的错误代码
#include<bits/stdc++.h>
#define int long long
int n, x;
const int maxn = 1e5 + 10;
int a[maxn], b[maxn];
int maxx = 1e4 + 10;
using namespace std;
int check(int y)
{
	//每次都从最远的开始看
	int now = 0;
	for (int i = n;i >= 0;i--)
	{
		if (a[i] > 0)
		{
			if ((i - now) <= y)
			{
				a[i]--;
				now = i;
				if (now == n) break;
				i = n+1;
				

			}

			/*else return false;*/

		}
		if (i == now) return 0;
	}
	if (now != n) return 0;

	for (int i = 0;i <= n;i++)
	{
		
		if (a[i] > 0)
		{
			if ((i - now) <= y)
			{
				a[i]--;
				now = i;
				
				if (now == 0) break;
				i = -1;
			}



		}
		if (i == now) return 0;
	}
	if (now != 0) return 0;
	return true;



}

int bin1(int l, int r)
{
	
	while (l < r)
	{int mid = (l + r) >> 1;
		if (check(mid)) r = mid;
		else l = mid + 1;
	}
	return r;
}
signed main()
{
	
	cin >> n >> x;
	a[0] = a[n] = maxx;
	for (int i = 1;i < n;i++)cin >> a[i];
	/*cout << "check"<<check(4);*/
	cout << bin1(1, maxn) << endl;

	return 0;
}
修改代码tle
#include <bits/stdc++.h>
#define int long long
int n, x;
const int maxn = 1e5 + 10;
int a[maxn], b[maxn];
using namespace std;

// 检查跳跃能力 y 是否满足要求
bool check(int y) {
    // 备份 a 数组
    for (int i = 0; i <= n; i++) {
        b[i] = a[i];
    }
    int now = 0;
    // 去学校的过程
    for (int k = 0; k < x; k++) {
        now = 0;
        while (now < n) {
            int next = -1;
            // 尽可能远地寻找能跳到的石头
            for (int i = n; i > now; i--) {
                if (b[i] > 0 && (i - now) <= y) {
                    next = i;
                    break;
                }
            }
            if (next == -1) {
                // 找不到能跳到的石头,返回 false
                return false;
            }
            b[next]--;
            now = next;
        }
    }
    // 回家的过程
    for (int k = 0; k < x; k++) {
        now = n;
        while (now > 0) {
            int next = -1;
            // 尽可能远地寻找能跳到的石头
            for (int i = 0; i < now; i++) {
                if (b[i] > 0 && (now - i) <= y) {
                    next = i;
                    break;
                }
            }
            if (next == -1) {
                // 找不到能跳到的石头,返回 false
                return false;
            }
            b[next]--;
            now = next;
        }
    }
    return true;
}

// 二分查找最小跳跃能力
int bin1(int l, int r) {
    while (l < r) {
        int mid = (l + r) >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    return r;
}

signed main() {
    cin >> n >> x;
    // 初始化起点和终点的石头高度
    a[0] = a[n] = x;
    for (int i = 1; i < n; i++) {
        cin >> a[i];
    }
    cout << bin1(1, n) << endl;
    return 0;
}
正解
#include <bits/stdc++.h>

#define i64 long long

using namespace std;

const int N = 1e5 + 5;

int n, x;
i64 arr[N], sum[N];

// 检查跳跃能力 y 是否满足要求
bool check(int y) {

    for (int i = y; i <= n - 1; i++) {
        if (sum[i] - sum[i - y] < 2 * x) {
            return false;
        }
    }
    return true;
}

/*
事实证明,从y开始枚举更好算
int check(int m)
{
	for (int i = 1;i <= n-m;i++)
	{
		if (a[i + m-1] - a[i-1] < 2 * x)return  0;
	}
	return 1;
}

*/

int main() {
//因为没对起点和终点检查,所以不用初始化
    cin >> n >> x;

    for (int i = 1; i <= n - 1; i++) {
        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;
}

5 冶炼金属

一直在错,因为ceil是向上取整,floor是向下取整。而且二分真假的返回条件不是!=或者=。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 10;
double  a[maxn], b[maxn];
int n;
int minn, maxx;
int check1(int x)
{
	for (int i = 1;i <= n;i++)
	{
		if (floor(a[i] / x) > b[i])
			return 0;
	}
	return 1;
}
int check2(int x)
{
	for (int i = 1;i <= n;i++)
	{
		if (floor(a[i] / x) < b[i])
			return 0;
	}
	return 1;
}
int main() {


	cin >> n;
	for (int i = 1;i <= n;i++)
	{
		int x, y;
		cin >> a[i] >> b[i];

	}
	int l = 1, r = 1e4 + 10;
	while (l < r)
	{
		int mid = (l + r) / 2;
		if (check1(mid) == 1)
			r = mid;
		else
			l = mid + 1;

	}
	minn = l;
	l = 1, r = 1e4 + 10;
	while (l < r)
	{
		int mid = (l + r + 1) / 2;
		if (check2(mid) == 1)
			l = mid;
		else
			r = mid - 1;

	}
	maxx = l;
	cout << minn << " " << maxx << endl;

}
posted on 2025-03-11 21:49  Hoshino1  阅读(19)  评论(0)    收藏  举报