二、二分与前缀和

在这里插入图片描述

数的范围

给定一个按照升序排列的长度为 n的整数数组,以及 q 个查询。对于每个查询,返回一个元素 k的起始位置和终止位置(位置从 0 开始计数)。如果数组中不存在该元素,则返回 -1 -1。
输入格式
第一行包含整数 n 和 q,表示数组长度和询问个数。
第二行包含 n个整数(均在 1∼10000范围内),表示完整数组。接下来 q 行,每行包含一个整数 k,表示一个询问元素。
输出格式
共 q 行,每行包含两个整数,表示所求元素的起始位置和终止位置。如果数组中不存在该元素,则返回 -1 -1。
数据范围
1≤n≤100000
1≤q≤10000
1≤k≤10000
输入样例:
6 3
1 2 2 3 3 4
3
4
5
输出样例:
3 4
5 5
-1 -1

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
const int N=100010;
using namespace std;
int a[N];
int n,m;
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++)  scanf("%d",&a[i]);
	while(m--)
//	for(int i=0;i<m;i++)
	{
		int x;
		cin>>x;
		int l=0,r=n-1;
		while(r>l)
		{
			int mid=r+l>>1; 
			if(a[mid]>=x)  r=mid;
			else l=mid+1;
		}
		if(a[l]==x)
		{
		  cout<<l<<" ";
		  r=n-1;
		  while(r>l)
		  {
		    int	mid=r+l+1>>1;     //加1  l=mid 
		    if(a[mid]<=x)  l=mid;
		    else r=mid-1;
		  }
		  if(a[l]==x)
		   cout<<l<<endl;
    	}
    	else cout<<"-1 -1"<<endl;
	}
}

数的三次方根

给定一个浮点数 n,求它的三次方根。
输入格式
共一行,包含一个浮点数 n。
输出格式
共一行,包含一个浮点数,表示问题的解。
注意,结果保留 6 位小数。
数据范围
−10000≤n≤10000
输入样例:
1000.00
输出样例:
10.000000

#include<iostream>
#include<cstring>
using namespace std;

int main()
{
    double n;
  // cin>>n;
  scanf("%lf",&n);               //一定是lf
    double l=-10000,r=10000;     //都定义为double
    while(r-l>1e-8)
    {
      double mid=(l+r)/2;
        if(mid*mid*mid>=n)
         r=mid;
         else l=mid;
         //cout<<mid<<endl;
    }
    printf("%lf",r);
    return 0;
}

前缀和

输入一个长度为 n的整数序列。接下来再输入 m 个询问,每个询问输入一对 l,r。对于每个询问,输出原序列中从第 l 个数到第 r 个数的和。
输入格式
第一行包含两个整数 n 和 m。
第二行包含 n 个整数,表示整数数列。
接下来 m 行,每行包含两个整数 l 和 r,表示一个询问的区间范围。
输出格式
共 m 行,每行输出一个询问的结果。
数据范围
1≤l≤r≤n,1≤n,m≤100000,−1000≤数列中元素的值≤1000
输入样例:
5 3
2 1 3 6 4
1 2
1 3
2 4
输出样例:
3
6
10

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
const int N=100010;
int a[N],s[N];
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)  
	{
    	cin>>a[i];
    	s[i]=s[i-1]+a[i];
   }
	while(m--)
	{
		int r,l;
		cin>>l>>r;
		cout<<s[r]-s[l-1]<<endl;
	}
 } 

子矩阵的和

输入一个 n行 m列的整数矩阵,再输入 q个询问,每个询问包含四个整数 x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。对于每个询问输出子矩阵中所有数的和。
输入格式
第一行包含三个整数 n,m,q。接下来 n 行,每行包含 m 个整数,表示整数矩阵。接下来 q 行,每行包含四个整数 x1,y1,x2,y2,表示一组询问。
输出格式
共 q 行,每行输出一个询问的结果。
数据范围
1≤n,m≤1000,1≤q≤200000,1≤x1≤x2≤n,1≤y1≤y2≤m,−1000≤矩阵内元素的≤1000
输入样例:
3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4
输出样例:
17
27
21

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
const int N=1010;
int a[N][N],s[N][N];
int main()
{
	int n,m,q;
	cin>>n>>m>>q;
	
	for(int i=1;i<=n;i++)  
	  	for(int j=1;j<=m;j++)
	   {
    	cin>>a[i][j];
    	s[i][j]=a[i][j]+s[i-1][j]+s[i][j-1]-s[i-1][j-1];	
       }
	while(q--)
	{
		int x1,y1,x2,y2;
		cin>>x1>>y1>>x2>>y2;
		int res=s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1];
		cout<<res<<endl;
	}
 } 

机器人跳跃问题

机器人正在玩一个古老的基于 DOS 的游戏。游戏中有 N+1座建筑——从 0 到 N 编号,从左到右排列。编号为 0 的建筑高度为 0 个单位,编号为 i 的建筑高度为 H(i) 个单位。起初,机器人在编号为 0 的建筑处。每一步,它跳到下一个(右边)建筑。假设机器人在第 k 个建筑,且它现在的能量值是 E,下一步它将跳到第 k+1 个建筑。如果 H(k+1)>E,那么机器人就失去 H(k+1)−E 的能量值,否则它将得到 E−H(k+1) 的能量值。游戏目标是到达第 N 个建筑,在这个过程中能量值不能为负数个单位。现在的问题是机器人至少以多少能量值开始游戏,才可以保证成功完成游戏?
输入格式
第一行输入整数 N。第二行是 N个空格分隔的整数,H(1),H(2),…,H(N)
代表建筑物的高度。
输出格式
输出一个整数,表示所需的最少单位的初始能量值上取整后的结果。
数据范围
1≤N,H(i)≤105,
输入样例1:
5
3 4 3 2 4
输出样例1:
4
输入样例2:
3
4 4 4
输出样例2:
4
输入样例3:
3
1 6 4
输出样例3:
3

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N=100010;
int h[N];
int n;
bool check(int mid)
{
    int res=mid;
    for(int i=1;i<=n;i++)
      {
          res=2*res-h[i];            //累加
          if(res>=1e5)   return true;  //不可漏,去掉可能会溢出
          if(res<0) return false;  //res<0不是res==0;
      }
      return true;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    scanf("%d",&h[i]);
    
    int l=1,r=1e5;
    while(l<r)
    {
       int  mid= r+l>>1;
        if(check(mid))  r=mid;
        else l=mid+1;
    }
    printf("%d",l);
}

*四平方和

四平方和定理,又称为拉格朗日定理:每个正整数都可以表示为至多 4 个正整数的平方和。如果把 0 包括进去,就正好可以表示为 4 个数的平方和。
比如:5=02+02+12+22,7=12+12+12+22对于一个给定的正整数,可能存在多种平方和的表示法。要求你对 4 个数排序:0≤a≤b≤c≤d并对所有的可能表示法按 a,b,c,d为联合主键升序排列,最后输出第一个表示法。
输入格式
输入一个正整数 N。
输出格式
输出4个非负整数,按从小到大排序,中间用空格分开。
数据范围
0<N<5∗106
输入样例:
5
输出样例:
0 0 1 2

二分

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=5*1e6+10;

struct sum{
	int s,c,d;
	bool operator< (const sum &t)const
	{
		if(s!=t.s)  return s<t.s;
		if(c!=t.c)    return c<t.c;
		return d<t.d;
	}
}sum[N];

int n,m;
int main()
{
	cin>>n;
	for(int c=0;c*c<=n;c++)
	  for(int d=c;d*d+c*c<=n;d++)
	    sum[m++]={c*c+d*d,c,d};
	
	sort(sum,sum+m);
	
	for(int a=0;a*a<=n;a++)
	  for(int b=a;a*a+b*b<=n;b++)
	  {
	  	int t=n-a*a-b*b;
	  	int l=0,r=m-1;
	  	while(r>l)
	  	{
	  		int mid=r+l>>1;
	  		if(sum[mid].s>=t) r=mid;
	  		else l=mid+1;
		  }
		  if(sum[l].s==t)
		  {
		  	printf("%d %d %d %d",a,b,sum[l].c,sum[l].d);
		  	return 0;
		  }
	  }
}

哈希表
c++11之前使用,需要加 #include<tr1/unordered_map>,
using namespace std::tr1;

#include<iostream>
#include<cstring>
#include<algorithm>
#include<tr1/unordered_map>
#define x first
#define y second
using namespace std;
using namespace std::tr1;

typedef pair<int ,int>PII;
unordered_map<int ,PII> S; 
const int N=5*1e6+10;
int n,m;

int main()
{
	cin>>n;
	for(int c=0;c*c<=n;c++)
	  for(int d=c;d*d+c*c<=n;d++)
	    {
	    	int t=c*c+d*d;
	    	if(S.count(t)==0)  S[t]={c,d};  //只存字典序最小的第一个 
	    }
	
	for(int a=0;a*a<=n;a++)
	  for(int b=a;a*a+b*b<=n;b++)
	  {
	  	int t=n-a*a-b*b;
	  	if(S.count(t))
		  {
		  	printf("%d %d %d %d",a,b,S[t].x,S[t].y);
		  	return 0;
		   } 
	  }
}
	
 

分巧克力

二分

#include<iostream>
#include<cstring>
#include<algorithm>
#define x first
#define y second
using namespace std;
const int N=1e5+10;
typedef pair<int ,int>PII;
PII a[N];
int n,k;
bool check(int b)
{
 int sum=0;
	for(int i=0;i<n;i++)
	{
		sum+=(a[i].x/b)*(a[i].y/b);    //乘法 一定要加括号
	}
	if(sum>=k)  return true;
	else return false;
}
int main()
{
	
	cin>>n>>k;
	for(int i=0;i<n;i++)  cin>>a[i].x>>a[i].y;
	int l=1,r=1e5;
	
	while(l<r)
	{
		int mid=r+l+1>>1;    //加一 
		if(check(mid))  l=mid;
		else r=mid-1;
	}

	cout<<r<<endl;
}

*激光炸弹

地图上有 N个目标,用整数 Xi,Yi表示目标在地图上的位置,每个目标都有一个价值 Wi。
注意:不同目标可能在同一位置。
现在有一种新型的激光炸弹,可以摧毁一个包含 R×R 个位置的正方形内的所有目标。激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆炸范围,即那个正方形的边必须和 x,y 轴平行。
求一颗炸弹最多能炸掉地图上总价值为多少的目标。
输入格式
第一行输入正整数 N和 R,分别代表地图上的目标数目和正方形包含的横纵位置数量,数据用空格隔开。
接下来 N 行,每行输入一组数据,每组数据包括三个整数 Xi,Yi,Wi,分别代表目标的 x 坐标,y 坐标和价值,数据用空格隔开。
输出格式
输出一个正整数,代表一颗炸弹最多能炸掉地图上目标的总价值数目。
数据范围
0≤R≤109
0<N≤10000,
0≤Xi,Yi≤5000
0≤Wi≤1000
输入样例:
2 1
0 0 1
1 1 1
输出样例:
1

#include<cstring>
#include<algorithm>
#include<iostream>

using namespace std;
const int N=5010;
int n,m;
int s[N][N];

int main()
{
	int cnt,R;
	cin>>cnt>>R;
	R=min (5001,R);
	
	//n和m
	n=m=R ;//保证右下角一定存在
	while(cnt--)
	{
		int x,y,w;
		cin>>x>>y>>w;
		x++,y++;
		n=max(n,x),m=max(m,y);
		s[x][y]+=w;
	 } 
	 
	 for(int i=1;i<=n;i++)
	   for(int j=1;j<=m;j++)
	     s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
     
	int res=0;
	//枚举所有边长是R的矩形,右下角 
	for(int i=R;i<=n;i++)
	  for(int j=R;j<=m;j++)
	    res=max(res,s[i][j]-s[i-R][j]-s[i][j-R]+s[i-R][j-R]);
	cout<<res<<endl;
	return 0; 
	
}    

*k倍区间

暴力

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100010;
int a[N],s[N];

int main()
{
    int count=0;
    int n,k;
  //  cin>>n>>k;
  scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        s[i]=s[i-1]+a[i];
    }
    for(int i=1;i<=n;i++)
       for(int j=1;j<=i;j++)
         {
             int m=s[i]-s[j-1];
             if(m%k==0)
             count++;
         }
         printf("%d",count);
         return 0;
        // cout<<count<<endl;
}

用空间换时间
当固定R时,在1~R中有多少个L满足(S[R]-S[L-1])%k=0
区间后移一位:
当固定R时,在0~R-1中有多少个L满足(S[R]-S[L])%k=0
即:有多少个S[R]与S[L]余数想等。
用long long int

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100010;
typedef long long int LL;
LL a[N],s[N],cnt[N];     //long long int 

int main()
{
    int count=0;
    int n,k;
  scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        s[i]=s[i-1]+a[i];
    }
    LL res=0;
    cnt[0]=1;             //注意
    for(int i=1;i<=n;i++)
    {
        res=res+cnt[s[i]%k];
        cnt[s[i]%k]++;
    }
         printf("%lld",res);
         return 0;
        // cout<<count<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值