2022/1/12 差分/二分

  1. 校门外的树

差分模板题

#include<iostream>
using namespace std;
const int N=1e4+10;
int a[N],b[N];
void insert(int l,int r,int c)
{
	b[l]+=c;
	b[r+1]-=c;
}
int main()
{
	int L,n;
	cin>>L>>n;
	
	//每个点都有一棵树 
	for(int i=1;i<=L+1;i++)//下标从一开始 
		a[i]=1;
	
	for(int i=1;i<=L+1;i++)
		b[i]=a[i]-a[i-1];
	
	while(n--)
	{
		int l,r;
		cin>>l>>r;
		l++,r++;//转化成从1开始
		insert(l,r,-1); 
	}
	//求出原数组 
	for(int i=1;i<=L+1;i++)
		a[i]=a[i-1]+b[i];
	
	//因为可能有重复区间,所以可能会出现某点的数量为负
	//把数量为负的变为0; 
	for(int i=1;i<=L+1;i++)
		a[i]=max(0,a[i]);
	
	int ans=0;
	for(int i=1;i<=L+1;i++)
		ans=ans+a[i];
		
	cout<<ans<<endl;

	return 0;
}

2.Tallest Cow
开始时假设所有奶牛的高度为最高的高度,如果两个奶牛可以相互看见,则他们中间的奶牛高度减1.

#include<iostream>
#include<set>
//#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int a[N],b[N];
void insert(int l,int r,int c)
{
	b[l]+=c;
	b[r+1]-=c;
}

int main()
{
	int n,I,h,p;
	cin>>n>>I>>h>>p;
	
	//初始化a数组 
	for(int i=1;i<=n;i++)
		a[i]=h;
	//初始化差分数组 
	for(int i=1;i<=n;i++)
		insert(i,i,a[i]);	
	
	
	//用来判断所给区间是否重复。
	set<pair<int,int> >st; //两个 > 之间有空格
	for(int i=0;i<p;i++)
	{
		int a,b;
		cin>>a>>b;
		
		if(a>b) swap(a,b); 
		
		if(!st.count({a,b}))
		{
			st.insert({a,b});
			insert(a+1,b-1,-1);//(a b)之间的高度减一所以是[a+1 b-1]这个区间 
		}
	}
	
	//求出原数组
	for(int i=1;i<=n;i++)
		a[i]=a[i-1]+b[i];
	
	//输出高度
	for(int i=1;i<=n;i++)
		cout<<a[i]<<endl;
	
		
	return 0;
}

3.铺设道路

构造差分序列:
b1=a1
b2=a2−a1
b3=a3−a2
……
bn=an−an−1
bn+1=−an (bn+1只是为了便于证明,并没有用到)

因为最终结果原数组都为零,所以相应的差分数组也都是零,因为对原数组一个区间进行操作,对应的是差分数组两个元素进行操作(b[l]- -,b[r+1]++)。所以问题可以转化为: 每次对差分数组中其中一个元素减1,另一个元素加1,经过多少次后,可以使所有差分数组元素都为零。因为这里我们构造的差分数组之和等于零( ∑bi=0),所以|∑b正|=|∑b负|(差分数组中正数之和等与负数之和),所以每次对正数减1的同时对负数加1,所以当正数减为0的同时负数也就加到了0,所以求出∑b正 即可.

#include<iostream>
using namespace std;
const int N=1e5+10;
int a[N],b[N]; 
int main()
{
	int  n;
	cin>>n;
	
	for(int i=1;i<=n;i++)
		cin>>a[i];
		
	//初始化差分数组 
	for(int i=1;i<=n;i++)
		b[i]=a[i]-a[i-1];
	
	int ans=0;
	//把大于0的求和
	for(int i=1;i<=n;i++)
	{
		if(b[i]>0) ans+=b[i];	
	}
	
	cout<<ans<<endl;
	return 0;	
}

4.地毯
二维差分模板题

#include<iostream>
using namespace std;
const int N=1010;
int b[N][N];
void insert(int x1,int y1,int x2,int y2)
{
	b[x1][y1]++;
	b[x2+1][y1]--;
	b[x1][y2+1]--;
	b[x2+1][y2+1]++;
}
int main()
{
	int n,m;
	cin>>n>>m;
	while(m--)
	{
		int x1,y1,x2,y2;
		cin>>x1>>y1>>x2>>y2;
		insert(x1,y1,x2,y2);
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			b[i][j]=b[i-1][j]+b[i][j-1]-b[i-1][j-1]+b[i][j];
		
	for(int i=1;i<=n;i++)
	{	
		for(int j=1;j<=n;j++)
			cout<<b[i][j]<<" ";
		
		cout<<endl; 
	}
			
	return 0;
}

5.假币问题
因为是最坏的情况,所以如果是奇数张最先抽出来的那个一定不是假币,假币在剩下的那些当中.因此如果是奇数张,把个数减1,剩下的和偶数张处理方式一样;
偶数张处理方式:用到了二分的思想,每次分为两份,取轻的那份继续分为两份,再取轻的那份继续此操作,直到每份为1;

#include<iostream>
using namespace std;
int main()
{
	int n;
	cin>>n;
	
	int cnt=0;
	while(n!=1)
	{
		if(n%2==1) n--;
		
		n=n/2;
		cnt++; 
	}
	cout<<cnt<<endl;
	return 0;
}

6.二分查找(一)
二分模版题

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
int n,m;
bool find(int x)
{
	int l=0,r=n-1;
	int mid;
	while(l<r)
	{
		mid=(l+r+1)/2;
		if(a[mid]<=x) l=mid;
		else r=mid-1;
	}
	if(a[l]==x) return true;
	 
	return false;
}
int main()
{
	
	cin>>n>>m;
	for(int i=0;i<n;i++)
		cin>>a[i];
	
	//需要先排序
	sort(a,a+n);
	 
	while(m--)
	{
		int x;
		cin>>x;
		if(find(x)) cout<<"YES"<<endl;
		else  cout<<"NO"<<endl;
	}

	return 0;
} 

7.查找最接近的元素
先找出大于等于x的那一个数a[l],所以最接近x的那个数一定是a[l]或者比a[l]小的那个数其中的一个

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];

int main()
{
	int n;
	cin>>n;
	
	for(int i=0;i<n;i++	)
		cin>>a[i];
	//因为题中已经说明是非降序列,所以不用再排序 
	
	int m;
	cin>>m;
	
	while(m--)
	{
		int x;
		cin>>x;
		
		int l=0,r=n-1,mid;
		//在a中查找>=x的数中最小的一个 
		while(l<r)
		{
			mid=(l+r)/2;
			if(a[mid]>=x)  r=mid;
			else l=mid+1;
		}

		//处理边界,因为下面用到了l-1,
		//所以特判l==0的情况 
		if(l==0) cout<<a[l]<<endl;
		else 
		{
			//因为a[l]是>=x的数中最小的一个
			//所以与x最接近的只可能是a[l],a[l-1]之间的一个 
			int ans=(abs(x-a[l-1])>abs(x-a[l]))? a[l]:a[l-1];
			cout<<ans<<endl;
		}	
		
	}
	return 0;	
} 

第六题模板与第七题模板区别
第六题模板是在序列a中查找<=x的数中最大的一个
第七题模板是在序列a中查找>=x的数中最小的一个

8.银行贷款
在这里插入图片描述

#include<iostream>
#include<cmath>
using namespace std;
double x,y,n;
double sum(double a)
{
	double ans=0;
	for(int i=1;i<=n;i++)
	{
		double res=pow((a+1),i);
		ans +=y/res;
	}
	return ans;
}
int main()
{
	scanf("%lf%lf%lf",&x,&y,&n);
	//百分数表示是   0到1000%
	//转化为小数就是 0到10 
	double l=0,r=10;
	while(r-l>1e-4)
	{
		double mid=(l+r)/2;
		//由上面式子可知:当sum(mid)>=x,应该更新l为mid
		if(sum(mid)>=x) l=mid; 
		else r=mid;	
	}
	//结果用百分数表示 
	printf("%.1lf\n",l*100);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值