暑假牛客训练

等我写完

目录

堆栈队列单调栈

1.栈和排序

2.牛牛与后缀表达式

二分、三分、01

1.[USACO 2009 Dec S]Music Notes

2.完全平方数

3.[NOIP2015]跳石头

4.[USACO 2017 Dec P]Greedy Gift Takers

5.[USACO 2010 Feb S]Chocolate Eating

6.华华给月月准备礼物

7.[CQOI2010] 扑克牌



堆栈队列单调栈

1.栈和排序

A-栈和排序

 首先预处理后缀最大值,之后遍历数组,如果该数是当前可插入的数的最大值则直接出栈(因为后面的数比他小,若先出会导致字典序减小),出栈后比较最大值左边的数和右边所有的数,如果右边(后边)等待插入的数里都没有大于左边的,那么先弹出左边的,直到条件不满足。否则插入右边的数字,重复操作直到数组遍历完成。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>

void solve(){
	int n;
	cin>>n;
	vector<int>a(n);
	vector<int>pos(n+1,0);
	stack<int>z;
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	for(int i=n-1;i>=0;i--){
		pos[i]=max(pos[i+1],a[i]);
	}
	for(int i=0;i<n;i++){
		z.push(a[i]);
		if(z.top()==pos[i]){
			cout<<z.top()<<" ";
			z.pop();	
		}
		while(!z.empty() and z.top()>pos[i+1]){
			cout<<z.top()<<" ";
			z.pop();
		}
	}
	while(!z.empty()){
		cout<<z.top()<<" ";
		z.pop();
	}
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);cout.tie(nullptr);
	int oyyo=1;
 	//cin>>oyyo;
	
	
	while(oyyo--) {
		solve();
	}
	return 0;
}

2.牛牛与后缀表达式

B-牛牛与后缀表达式

设立一个栈,然后遍历字符串,在遍历过程中创建一个临时变量来记录字符串里要计算的数字,如果碰到了结束符,那么就把所记录的数字插入栈中,如果碰到了计算符号,就跳出前两个数字,进行运算。并将结果继续推入到栈中。最后输出栈顶元素,即为最终结果。P.S.运算符是“后”(符)“前”。例如代码中的“-”。

long long legalExp(string str) {
	// write code here
	stack<long long>que;
	long long len=str.size();
	for(int i=0;i<len;i++){
		long long ans=0;
		while(str[i]>='0' and str[i]<='9'){
			ans=ans*10+str[i]-'0';
			i++;
		}
		if(str[i]=='#'){
			que.push(ans);
		}if(str[i]=='+'){
			long long a=que.top();que.pop();
			long long b=que.top();que.pop();
			que.push(a+b);
		}else if(str[i]=='-'){
			long long a=que.top();que.pop();
			long long b=que.top();que.pop();
			que.push(a-b);
		}else if(str[i]=='*'){
			long long a=que.top();que.pop();
			long long b=que.top();que.pop();
			que.push(a*b);
		}
	}
	return que.top();
}

二分、三分、01

二分查找算法细节与查找左右侧边界_二分查找边界-CSDN博客

1.[USACO 2009 Dec S]Music Notes

P2969 [USACO09DEC] Music Notes S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

利用前缀和还有二分,一共有N个音阶,并且每个音阶的敲击次数已知,那么利用前缀和就可以直到每个音阶在什么时间段被敲击。利用lower_bound可以知道这个找到这个时间段所对应的数组下标(音阶编号)按照输出要求对结果+-1就好,这个是数组范围的过。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
const int mod=998244353;
int gcd(int a,int b){
	return b>0?gcd(b,a%b):a;
}
int lcm(int a,int b){
	return a/gcd(a,b)*b;
}
int ksm(int b,int p){
	int res=1;
	while(p>0){
		if(p&1){
			res*=b;
			//res%=mod;
		}
		b*=b;
		//b%=mod;
		p>>=1;
	}
	return res;
}

void solve(){
	int n,q;
	cin>>n>>q;
	vector<int>b(n);
	vector<int>t(q);
	for(int i=0;i<n;i++){
		cin>>b[i];
	}
	for(int i=0;i<q;i++){
		cin>>t[i];
	}
	vector<int>c(n,0);
	c[0]=b[0]-1;
	for(int i=1;i<n;i++){
		c[i]=c[i-1]+b[i];
	}
	for(int i=0;i<q;i++){
		int k=lower_bound(c.begin(),c.end(),t[i])-c.begin();
		cout<<k+1<<endl;
	}
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);cout.tie(nullptr);
	int oyyo=1;
// 	cin>>oyyo;
	while(oyyo--) {
		solve();
	}
	return 0;
}

2.完全平方数

题目描述

多次查询[l,r]范围内的完全平方数个数

定义整数x为完全平方数当且仅当可以找到整数y使得y*y=x

输入

第一行一个数n表示查询次数
之后n行每行两个数l,r

输出

对于每个查询,输出一个数表示答案

示例1

输入

5
1 3
1 4
2 4
4 4
1 1000000000

输出

1
2
1
1
31622
n <= 100000
0<= l <= r <= 1000000000

 l和r开方再判断边界,两个边界数直接相减就好

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
const int mod=998244353;
int gcd(int a,int b){
	return b>0?gcd(b,a%b):a;
}
int lcm(int a,int b){
	return a/gcd(a,b)*b;
}
int ksm(int b,int p){
	int res=1;
	while(p>0){
		if(p&1){
			res*=b;
			//res%=mod;
		}
		b*=b;
		//b%=mod;
		p>>=1;
	}
	return res;
}

void solve(){
	int n;
	cin>>n;
	vector<int>l(n),r(n);
	for(int i=0;i<n;i++){
		cin>>l[i]>>r[i];
	}
	for(int i=0;i<n;i++){
		int k2=sqrt(r[i]);
		int k1=sqrt(l[i]);
		if(k1*k1<l[i]){
			k1++;
		}
		cout<<k2-k1+1<<endl;
	}
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);cout.tie(nullptr);
	int oyyo=1;
// 	cin>>oyyo;
	while(oyyo--) {
		solve();
	}
	return 0;
}

3.[NOIP2015]跳石头

[NOIP2015 提高组] 跳石头 - 洛谷

二分,做之前就知道,但还是没写出。

先二分确定距离,再编历看要搬的石头数满足题意吗。距离确定了,就把间距小于确定距离的需要全部搬走。最后看要搬走的石头数量是不是比最多可搬走的数量小,若小,那么就把间距拉大。否则减小。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
const int mod=998244353;

int l,n,m;
vector<int>d(50010,0);
bool judge(int x){
	int tot=0,i=0,now=0;
	while(i<n+1){
		i++;
		if(d[i]-d[now]<x){
			tot++;
		}else{
			now=i;
		}
	}
	if(tot>m){
		return false;
	}else{
		return true;
	}
}
void solve(){
	
	cin>>l>>n>>m;
	
	for(int i=1;i<=n;i++){
		cin>>d[i];
	}
	d[n+1]=l;
//	for(int i=0;i<=n;i++){
//		cout<<e[i]<<" ";
//	}
	int le=1,r=l,mid;
	int ans=0;
	while(le<=r){
		mid=(le+r)/2;
		if(judge(mid)){
			ans=mid;
			le=mid+1;
		}
		else{
			r=mid-1;
		}
	}
	cout<<ans<<endl;
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);cout.tie(nullptr);
	int oyyo=1;
// 	cin>>oyyo;
	while(oyyo--) {
		solve();
	}
	return 0;
}

4.[USACO 2017 Dec P]Greedy Gift Takers

[USACO17DEC] Greedy Gift Takers P - 洛谷

一头牛不能拿到礼物一定因为前面的牛构成循环。

可以二分这个形成循环的长度,然后判断这个循环是过短还是过长。

如何判断这个循环过短还是过长?

bool check(int x){//循环长度
    if(x==1){//排在第一个的牛肯定能拿到礼物
        return 1;
    }
    for(int i=1;i<x;i++){
        b[i]=a[i];
    }
    sort(b+1,b+x);
    //从小到大排序,此时不关注哪一头牛被放到了后面,我们关注是否有牛被放到二分的循环长度之外 
    int limit=n-x;//拿不到牛的数量 
    for(int i=1;i<x;i++){
        if(b[i]>limit){
            return false;
            //这个循环中有拿不到礼物的牛,
            //因为即使轮到它后它可以排到很前面,
            //但因为前面已经成循环了,所以轮不到它 
        }
        limit++;//有牛被放到循环后面了,后面的牛都可以往前移动一格
    }
    return true;
}

现在二分了一个循环长度 now ,如果这个循环过长,实际上的循环更短,那么一种情况就是这一个循环中被放到最后面的元素仍在一些元素前面,那么这些元素即使拿礼物后可以放到比较前面,但他们没有拿礼物的机会(前面自成一个循环)。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
const int mod=998244353;
int n;
int a[100005],b[100005],l,r,mid;
bool check(int x){//循环长度
	if(x==1){//排在第一个的牛肯定能拿到礼物
		return 1;
	}
	for(int i=1;i<x;i++){
		b[i]=a[i];
	}
	sort(b+1,b+x);
	//从小到大排序,此时不关注哪一头牛被放到了后面,我们关注是否有牛被放到二分的循环长度之外 
	int limit=n-x;//拿不到牛的数量 
	for(int i=1;i<x;i++){
		if(b[i]>limit){
			return false;
			//这个循环中有拿不到礼物的牛,
			//因为即使轮到它后它可以排到很前面,
			//但因为前面已经成循环了,所以轮不到它 
		}
		limit++;//有牛被放到循环后面了,后面的牛都可以往前移动一格
	}
	return true;
}
void solve(){
	cin>>n;
	int ans;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	int l=1,r=n;
	while(l<=r){
		mid=(l+r)/2;
		if(check(mid)){
			ans=mid;
			l=mid+1;
		}else{
			r=mid-1;
		}
	}
	cout<<n-ans<<endl;
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);cout.tie(nullptr);
	int oyyo=1;
// 	cin>>oyyo;
	while(oyyo--) {
		solve();
	}
	return 0;
}

5.[USACO 2010 Feb S]Chocolate Eating

[USACO10FEB] Chocolate Eating S - 洛谷zz

这个玩意他一定会把巧克力吃完

左边界先设为0,右边界就是所有巧克力开心值的累加和,二分出mid,如果开心值小于mid就吃,大于等于就不吃。

要在每次循环前加一个for来给zz(当前这块巧克力是第几天吃的)从新赋值为0,以避免输出时出现少一块巧克力或者多一块巧克力的情况。

要再扫一遍ans,因为不扫的话最后每个巧克力要在第几天吃可能会停在上一个阶段。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
const int mod=998244353;
int n,d;
int a[500005];
int l,r,mid,ans;
int c[500005];
int zz[500005];
bool check(int x){
	int sum=0;
	int j=0;//外指针。
	for(int i=1;i<=n;i++)
		zz[i]=0;
	for(int i=1;i<=d;i++){
		sum/=2;//每天开心值会减半。
		while(sum<x){//如果开心值小于二分值就吃巧克力
			j++;
			sum+=a[j];
			zz[j]=i;
			if(j>n){
				return false;//不够吃就错了。
			}
		}
	}
	return true;//情况可以满足。
}
void solve(){
	cin>>n>>d;
	int sum=0;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		sum+=a[i];
	}
	int l=0,r=sum;
	while(l<=r){	
		mid=l+(r-l)/2;
		if(!check(mid)){
			r=mid-1;
		}
		if(check(mid)){
			ans=mid;
			l=mid+1;
		}
	}
	
	cout<<ans<<endl;
	check(ans);
	for(int i=1;i<=n;i++){
		if(zz[i]!=0){
			cout<<zz[i]<<endl;
		}else{
			cout<<d<<endl;
			//如果这个巧克力在二分结果内不用吃,那么就放最后一天吃,
			//她一定会吃完。
		}
	}	
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);cout.tie(nullptr);
	int oyyo=1;
// 	cin>>oyyo;
	while(oyyo--) {
		solve();
	}
	return 0;
}

6.华华给月月准备礼物

登录—专业IT笔试面试备考平台_牛客网

这个算是比较简单的二分了,他手上有一些木棍,每个被裁的越长越好,那么就枚举长度,每次都加上可以切出的个数,如果二分长度为0,就直接return true(一定会满足),否则会有÷0浮点错误。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
const int mod=998244353;
int n,k;
int a[200005],l,r,mid,ans;

bool check(int x){
	if(x==0){
		return true;
	}
	int sum=0;
	for(int i=0;i<n;i++){
		if(a[i]>=x){
			sum+=a[i]/x;
		}
	}
	if(sum>=k){
		return true;
	}else{
		return false;
	}
}

void solve(){
	cin>>n>>k;
	int sum=0;
	for(int i=0;i<n;i++){
		cin>>a[i];
		sum+=a[i];
	}
	if(sum<k){
		cout<<0<<endl;
		return;
	}
	int l=0,r=sum;
	while(l<=r){
		mid=(l+r)/2;
		if(check(mid)){
			ans=mid;
			l=mid+1;
		}else{
			
			r=mid-1;
		}
	}
	cout<<ans<<endl;
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);cout.tie(nullptr);
	int oyyo=1;
// 	cin>>oyyo;
	while(oyyo--) {
		solve();
	}
	return 0;
}

7.[CQOI2010] 扑克牌

[CQOI2010] 扑克牌 - 洛谷

二分套牌的数目,如果题目的joker数可以满足要求,就return true,另:X套牌中最多只能有X个joker

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define pii pair<int,int>
const int mod=998244353;

int n,m;
int a[55],l,r,mid,ans;
bool check(int x){
	int t=m;
	int res=x;
	for(int i=1;i<=n;i++){
		if(a[i]<x){
			t-=(x-a[i]);
			res-=(x-a[i]);
		}
		if(t<0 or res<0){
			return false;
		}
	}
	return true;
}

void solve(){
	cin>>n>>m;
	int max1=m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		max1+=a[i];
	}
	l=1,r=max1;
	while(l<=r){
		mid=(l+r)/2;
		if(check(mid)){
			ans=mid;
			l=mid+1;
		}else{
			r=mid-1;
		}
	}
	cout<<ans<<endl;
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);cout.tie(nullptr);
	int oyyo=1;
// 	cin>>oyyo;
	while(oyyo--) {
		solve();
	}
	return 0;
}

  • 25
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值