寒假训练知识点&题解
文章目录
1.2
-
priority_queue<int,vector<int>,greater<int> >q
优先队列q.pop()
q.push()
q.top()
小->大 -
priority_queue<int> a;
a.empty() a.size()
大->小 -
vector<int>a
vecotr数组
a.insert(upper_bound(a.begin(),a.end(),x),x);
二分插入X -
单调队列:
队列内可以是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
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)
常用的三个函数:
binary_search(数组名+n1,数组名+n2,值) - 数组名
查找等于(!a>b&&!a<b—a排在b前后都行)值的元素,找到返回true,否则falselower_bound(数组名+n1,数组名+n2,值,排序函数bool cmp ) - 数组名
二分查找下界 (大于等于值) 返回值是一个指针 可以减去 数组名+n1 得到下标。找不到即 数组名+n2。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;
}