寒假训练知识点&题解

寒假训练知识点&题解

1.2

  1. priority_queue<int,vector<int>,greater<int> >q 优先队列 q.pop() q.push() q.top() 小->大

  2. priority_queue<int> a; a.empty() a.size() 大->小

  3. vector<int>a vecotr数组
    a.insert(upper_bound(a.begin(),a.end(),x),x); 二分插入X

  4. 单调队列:

    队列内可以是node、下、前缀和下标

    deque<node>vis; //单调队列 存放递增的 node :order value
    if(!vis.empty() && i-vis.front().order>=m) vis.pop_front();
    while(!vis.empty() && vis.back().value>a[i]) vis.pop_back(); //保持队列递增
    vis.push_back(tmp);
    printf("%d\n",vis.front().value); 输出队首(最大或者最小)

1.3

  1. vector<int>a; a.push_back(x) a.size() a.erase(a.begin() )
    find(a.begin() ,a.end() ,x)==a.end()

A - 切蛋糕

前缀和下标+单调队列 求区间最值

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10,INF=1e8;
int sum[maxn];
deque<int>q;
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;++i)
	{
		int x;
		scanf("%d",&x);
		sum[i]=sum[i-1]+x;
	}
	
	int ans=-INF;
	q.push_back(0);
	for ( int i=1;i<=n;++i)
	{
		while (!q.empty()&&q.front()<i-m) q.pop_front();
		ans=max(ans,sum[i]-sum[q.front()]);
		while (!q.empty()&&sum[i]<=sum[q.back()]) q.pop_back();
		q.push_back(i);
	}
	printf("%d\n",ans);
	return 0;
}

B - 好消息,坏消息

前缀和思维题

#include<bits/stdc++.h>
using namespace std;
const int maxn=1000001;
long long n;
long long a[maxn];
long long suma[maxn];
long long min_1k[maxn],min_kn[maxn];
int main(){
    scanf("%lld",&n);
    min_1k[0]=LONG_LONG_MAX>>1;
	min_kn[n+1]=LONG_LONG_MAX>>1;;
    
    for (int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        suma[i]=suma[i-1]+a[i];
        min_1k[i]=min(suma[i],min_1k[i-1]);
    }
    for (int i=n;i>=1;i--) {
    	min_kn[i]=min(min_kn[i+1],suma[i]);
	}
    int ans=0;
    for (int k=1;k<=n;k++) 
		if ((min_kn[k]-suma[k-1]>=0)&&(min_1k[k-1]+suma[n]-suma[k-1]>=0)) ans++;
		
    printf("%d\n",ans);
    return 0;
}

D - 机器翻译

结合vector的特点,有点思维

#include<bits/stdc++.h>
using namespace std;
int n,m,ans=0;
vector<int>a;
int main(){
	cin>>m>>n;
	for(int i=0;i<n;i++){
		int x;
		cin>>x;
		if(find(a.begin() ,a.end() ,x)==a.end() ){
			ans++;
			a.push_back(x);
			if(a.size()>m){
				a.erase(a.begin() ); 
			} 
		}
	}
	cout<<ans<<endl;	
}

F 蚯蚓

三个队列+题目隐含具有单调性

#include<stdio.h>
#include<queue>
#include<algorithm>
using namespace std;
int n,m,q,u,v,t;
queue <int> q1;
queue <int> q2;
queue <int> q3;
inline int maxnum()
{
	int res=-0x3f3f3f3f;
	int num=0;
	if(!q1.empty()&&res<q1.front()) res=q1.front(),num=1;
	if(!q2.empty()&&res<q2.front()) res=q2.front(),num=2;
	if(!q3.empty()&&res<q3.front()) res=q3.front(),num=3;
	if(num==1)      q1.pop();
	else if(num==2) q2.pop();
	else if(num==3) q3.pop();
	return res;
}
int a[100000];int now;int j;
int main()
{
	scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);
	for(int i=0;i<n;i++){
		scanf("%d",&a[i]);
	}
	sort(a,a+n);
	for(int i=n-1;i>=0;i--){
		q1.push(a[i]);
	}
	for(int i=1;i<=m;i++,j+=q){
		int te=maxnum()+j;
		if(i%t==0) printf("%d ",te);
		int ma=max(1LL*te*u/v-j-q,te-1LL*te*u/v-j-q);q2.push(ma);//大的放q2
		int mi=min(1LL*te*u/v-j-q,te-1LL*te*u/v-j-q);q3.push(mi);
	}
	printf("\n");
	for(int i=1;!q1.empty()||!q2.empty()||!q3.empty();i++)
	{ 
		int res=maxnum();
		if(i%t==0)  printf("%d ",res+j);
	}
	return 0;
} 

I - Balanced Lineup G

J - Balanced Lineup

两题都是倍增RMQ求区间最值

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<string.h>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<map>
typedef long long ll;
using namespace std;
#define INF 0x3f3f3f3f

int n,q;
int rmqmin[50005][16];
int rmqmax[50005][16];
int lo[50005];
//初始化
void rmqinit(){
    for (int j = 1; (1<<j) <= n; ++j) 
    	for (int i = 1; i + (1 << j) - 1 <= n; ++i){
			 rmqmax[i][j] = max(rmqmax[i][j - 1], rmqmax[i + (1 << j - 1)][j - 1]);
             rmqmin[i][j] = min(rmqmin[i][j - 1], rmqmin[i + (1 << j - 1)][j - 1]);
		 }
}

//调用
int rmi(int l,int r){
    //int k=log2(l-r+1)
	int k=lo[r-l+1];
    return min(rmqmin[l][k],rmqmin[r-(1<<k)+1][k]);
}

int rma(int l,int r){
    //int k=log2(l-r+1)
	int k=lo[r-l+1];
    return max(rmqmax[l][k],rmqmax[r-(1<<k)+1][k]);
}
int main(){ 
	cin>>n>>q;
	//构造
	for(int i=1;i<=n;i++){
		cin>>rmqmax[i][0];
  	  	rmqmin[i][0]=rmqmax[i][0];
	}
	lo[1]=0;
	for(int i=2;i<=n;i++){
		lo[i]=lo[i/2]+1;//打表log2的值
	}
	rmqinit();
	while(q--){
		int l,r;
		cin>>l>>r;
		cout<< rma(l,r)-rmi(l,r) <<endl;
	}

	return 0; 
}

1.16-VJ-部分解题思路


VJ训练链接: 2023寒假集训9(1月16日)-算法竞赛-2.3二分法、2.9分治法-cf<1800

二分 OI Wiki 知识点链接 : 二分

简洁的二分模板

//给出单调递增的整数序列 ,查找第一个大于或等于 的数的位置。
while(l<r){ 
    int mid=(l+r)>>1;//l表示区间左端点,r表示区间右端点 
    if(a[mid]>=x){//x为目标值 
  //if(check(x))
         r=mid; 
    }
    else{
        l=mid+1;
    } 
}
//区间[l,r)

常用的三个函数:

  1. binary_search(数组名+n1,数组名+n2,值) - 数组名
    查找等于(!a>b&&!a<b—a排在b前后都行)值的元素,找到返回true,否则false
  2. lower_bound(数组名+n1,数组名+n2,值,排序函数bool cmp ) - 数组名 二分查找下界 (大于等于值) 返回值是一个指针 可以减去 数组名+n1 得到下标。找不到即 数组名+n2。
  3. upper_bound(数组名+n1,数组名+n2,值,排序函数bool cmp) - 数组名 二分查找上界 即 大于值

A - 跳石头

题目:在起点和终点之间,有 N 块岩石(不含起点和终点的岩石),组委会至多从起点和终点之间移走 M 块岩石(不能移走起点和终点的岩石)。求最短跳跃距离的最大值。

思路:二分答案,check中判断:最小距离为mid时,需要移走的石头数目(num),num<=m , 答案成立。

注意: check判断时候需要加上终点

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; 
const int maxn=2000000;
int a[maxn];
int l,n,m,ans;
bool check(int mid){
	int now=0,num=0;
	for(int i=1;i<=n+1;i++){      //注意 n+1
		if(a[i]-now<mid) num++;
		else now=a[i];
	}
	return num<=m;
}

int main(){

	cin>>l>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	a[n+1]=l;//终点
	int left=0,right=l;
	while(left<=right){
		int mid=(left+right)/2;
		if(check(mid)){
			left=mid+1;
			ans=mid;
		}
		else right=mid-1;
	}
	 printf("%d\n",ans);
	return 0;
}
 

B - 聪明的质监员

C- 饥饿的奶牛

思路:DP。 dp i 表示i为终点 的最大草堆数目 ,各个草堆按照起点排序,枚举可能的终点(枚举每一个草堆区间),更新草堆终点的dp值。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; 
const int maxn=3000005;
int n,maxy,dp[maxn],ans;//dp i 表示i为终点 的最大草堆数目 
struct Cow{
    int x,y;
	int len;
};
Cow a[150005];
bool cmp(Cow p,Cow q){
	if( p.x==q.x) return p.y<q.y;
	else return p.x<q.x;
}
int main(){
    scanf ("%d",&n);
    for (int i=0;i<n;i++){
    	scanf ("%d%d",&a[i].x,&a[i].y);
		a[i].len=a[i].y-a[i].x+1;
    	maxy=max(maxy,a[i].y);  //找到最大y
    }
    sort(a,a+n,cmp);  //按照起点排序
    int j=0;
    for (int i=0;i<=maxy;i++){     //枚举终点 
        dp[i]=max(dp[i],dp[i-1]);  //答案向后传递
        while (a[j].x==i&&j<n){ 
            dp[a[j].y]=max(dp[a[j].y],dp[i-1]+a[j].len); //加不加这段
            j++;
        }
    }
    printf ("%d",dp[maxy]);//输出
    return 0;
}
 

D - 分梨子

E- Monthly Expense

题意:给你一个长度为N的序列,现在需要把他们切割成M个子序列(所以每一份都是连续的),使得每个子序列和均不超过某个值X

思路:二分答案x

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
int a[100005];
int n,m;
bool check(long long x){
	ll sum=0,cnt=1;
	for(int i=1;i<=n;)
		if(sum+a[i]<=x){   //sum小于 x 那就继续加
			sum+=a[i];
			i++;
		}
		else{  //sum大于 x 
			sum=0;     //和清零
			cnt++;    //子序列个数++
			if(cnt>m) return 0;
		}
	}
	return cnt<=m;
	
}
int main()
{	
	
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	ll l=0,r=1e10;
	while(l<=r){  //二分
		ll mid=(l+r)/2;
		if(check(mid))r=mid-1;
		else l=mid+1; 
	}
	cout<<l<<endl;
    return 0;
}

F - Inversion

题意:可以交换K次 求逆序对数

思路:举例发现 交换K次最多减少K个逆序对数目 这样题目就转化为I 题

#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll; 
const int maxn=5e5+5;
ll a[500005];
ll myrank[500005];
ll k,n;
ll ans=0;
void DFS(int s,int t){
	if(s==t) return ;
	int mid=s+t>>1;
	DFS(s,mid),DFS(mid+1,t);//递归
	int i=s,j=mid+1,k=s;    //二路归并 
	while(i<=mid&&j<=t)          
		if(a[i]<=a[j]) myrank[k++]=a[i++];
		else myrank[k++]=a[j++],ans+=(ll)mid-i+1;//顺带计算答案
	while(i<=mid) myrank[k]=a[i],k++,i++;
	while(j<=t) myrank[k]=a[j],k++,j++;
	for(int i=s;i<=t;i++) a[i]=myrank[i];
}
int main(){
	while(scanf("%d%d",&n,&k)!=EOF){
		ans=0;
		memset(a,0,sizeof a);
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		DFS(1,n);
		if(ans<=k)printf("0\n");	
		else printf("%lld\n",ans-k);	
	}
	return 0;
}
 

G - Who’s in the Middle

题意:签到题,求中位数

思路:sort 直接输出a[n/2]

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll; 
const int N=10005;
int a[N];
int main(){
	int n;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	sort(a,a+n);
	cout<<a[n/2]<<endl;
	return 0;
}
 

H - 最大子段和

思路:dp

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; 
const int maxn=5e5+5;
int a[maxn];//dp 数组  a[i] 表示遍历长度为 i 时最大的区间和 
int sum;
int main()
{
    int n;
    cin>>n;
    cin>>a[1];
    sum=a[1];
    for(int i=2;i<=n;++i)
    {
        if(sum<0) sum=0; //加成负的了 说明上一个数字太小了 不要了 从头开始 
        scanf("%d",&a[i]);
        sum+=a[i]; 
        a[i]=max(a[i-1],sum);//DP 
    }
    printf("%d",a[n]);//i==n 时候就是答案 
    return 0;
} 

I - 逆序对

题意:求逆序对数

思路:递归+二路归并

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; 
const int maxn=5e5+5;
int a[500005],myrank[500005],n;
ll ans=0;
void DFS(int s,int t){
	if(s==t) return ;
	int mid=s+t>>1;
	DFS(s,mid),DFS(mid+1,t);//递归
	int i=s,j=mid+1,k=s;    //二路归并 
	while(i<=mid&&j<=t)          
		if(a[i]<=a[j]) myrank[k++]=a[i++];
		else myrank[k++]=a[j++],ans+=(ll)mid-i+1;
	while(i<=mid) myrank[k]=a[i],k++,i++;
	while(j<=t) myrank[k]=a[j],k++,j++;
	for(int i=s;i<=t;i++) a[i]=myrank[i];
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	DFS(1,n);
	printf("%lld\n",ans);
	return 0;
}
 

J - 因子和

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值