20211104NOIP模拟赛
上一次考到了135分(还挂了60分),这次就直接0分啦~~
没关系,NOIP好好干!!(放屁!看错题了导致一分没有!区间最大值我以为是前缀积!)
第一题:
由于我只会第一题所以我好好讲一下。
算法一: 玄学二分答案l,在二分时线段树查询变成的时间复杂度。
期望得分:75~100(必须吸氧!!还要卡常)。
放代码
点击查看代码
#include<bits/stdc++.h>
#define in read()
#define MAXN 2000006
// #define int long long
using namespace std;
const int INF=1.6e9;
namespace IO{//忽略又臭又长的快读
char buf[2000010],*cur=buf+2000010;
inline char getc(){
(cur==buf+2000010)?fread(cur=buf,1,2000010,stdin):0;
return *cur++;
}
char buff[2000010],*curr=buff;
inline void flush(){
fwrite(buff,1,curr-buff,stdout);
}
inline void putc(const char &ch){
(curr==buff+2000010)?fwrite(curr=buff,1,2000010,stdout):0;
*curr++=ch;
}
inline void rd(int &x){
x=0;char ch=getc();int f=1;
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getc();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getc();
}
x*=f;
}
char st[60];int tp;
void PT(int x){
if(x<0)putc('-'),x=-x;
if(x==0)putc('0');
else{
while(x>0){
st[++tp]=x%10+'0';
x/=10;
}
}
while(tp)putc(st[tp--]);
}
}
using IO::getc;
using IO::putc;
using IO::rd;
using IO::PT;
int n,m,lask,rask,ladd,radd,addnum,maxnum=-INF,minnum=INF;
int treev[MAXN][2],treel[MAXN][2];//一个是max一个是tag
inline void down(int k,int id)
{
treel[(k<<1)][id]+=treel[k][id],treel[k<<1|1][id]+=treel[k][id];
treev[k<<1][id]+=treel[k][id],treev[k<<1|1][id]+=treel[k][id];
treel[k][id]=0;
}
inline void add(int k,int id,int L,int R)
{
if(L>=ladd&&R<=radd)
{
treev[k][id]+=addnum;
treel[k][id]+=addnum;
maxnum=max(maxnum,treev[k][id]),minnum=min(minnum,treev[k][id]);
// cerr<<"Tree:"<<id<<" id:"<<k<<" L:"<<L<<" R:"<<R<<" v:"<<treev[k][id]<<'\n';
return;
}
if(treel[k][id]) down(k,id);
int mid=(L+R)>>1;
if(ladd<=mid) add((k<<1),id,L,mid);
if(radd>mid) add((k<<1)+1,id,mid+1,R);
treev[k][id]=max(treev[k<<1][id],treev[k<<1|1][id]);
// cerr<<"Tree:"<<id<<" id:"<<k<<" L:"<<L<<" R:"<<R<<" v:"<<treev[k][id]<<'\n';
}
int l,r,ans;
inline int findA(int mid,int pos,int L,int R)//寻找mid在A中的位置
{
if(l<=L&&R<=r)
{
if(treev[pos][0]<=mid) return R;
if(L==R) return L-1;
down(pos,0);
int mmm=(L+R)>>1;
if(treev[pos<<1][0]<=mid) return findA(mid,pos<<1|1,mmm+1,R);
else return findA(mid,pos<<1,L,mmm);
}
down(pos,0);
int mmm=(L+R)>>1,tmp;
if(l<=mmm) tmp=findA(mid,pos<<1,L,mmm);
if(mmm<r&&(l>mmm||tmp==mmm)) tmp=findA(mid,pos<<1|1,mmm+1,R);
return tmp;
}
inline int findB(int mid,int pos,int L,int R)//寻找mid在B中的位置
{
if(l<=L&&R<=r)
{
if(treev[pos][1]<=mid) return L;
if(L==R) return R+1;
down(pos,1);
int mmm=(L+R)>>1;
if(treev[pos<<1|1][1]<=mid) return findB(mid,pos<<1,L,mmm);
else return findB(mid,pos<<1|1,mmm+1,R);
}
down(pos,1);
int mmm=(L+R)>>1,tmp;
if(mmm<r) tmp=findB(mid,pos<<1|1,mmm+1,R);
if(l<=mmm&&(r<=mmm||tmp==mmm+1)) tmp=findB(mid,pos<<1,L,mmm);
return tmp;
}
signed main()
{
// freopen("girl3.in","r",stdin);
// freopen("girl.out","w",stdout);
rd(n),rd(m);
for(int i=1;i<=n;++i) ladd=radd=i,rd(addnum),add(1,0,1,n);
for(int i=1;i<=n;++i) ladd=radd=i,rd(addnum),add(1,1,1,n);
register int lll,rrr,mmm;
char opt;
for(int i=0;i<m;++i)
{
opt=getc();
while(opt!='A'&&opt!='B') opt=getc();
rd(ladd),rd(radd),rd(addnum);
if(opt=='A') add(1,0,1,n);//插入两个线段树中
else add(1,1,1,n);
rd(l),rd(r);
lll=minnum,rrr=maxnum;
while(lll<rrr)//二分答案
{
mmm=(lll+rrr)>>1;
if(findA(mmm,1,1,n)+1>=findB(mmm,1,1,n)) rrr=mmm;//他在A中的位置在B的后面说明可行
else lll=mmm+1;
}
PT(lll),putc('\n');
}
IO::flush();
return 0;
}
算法二:玄学的做法,但是很容易被卡。
期望得分:100(随机数据,不然卡成);
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define lc k<<1
#define rc k<<1|1
//#pragma GCC optimize(2)
#define num ch-'0'
void get(int &res)
{
char ch;bool flag=0;
while(!isdigit(ch=getchar()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getchar());res=res*10+num);
(flag)&&(res=-res);
}
const int N=6e5+5,inf=0x3f3f3f3f;
int n,m,a[N],b[N];
struct node{
int mx,pos;
};
struct TREE{//封装线段树
struct nde{
int l,r,mx,pos,lz;
}t[N<<2];
void pushup(int k){
if(t[lc].mx>=t[rc].mx){
t[k].mx=t[lc].mx;
t[k].pos=t[lc].pos;
}
else{
t[k].mx=t[rc].mx;
t[k].pos=t[rc].pos;
}
}
void pushdown(int k){
if(t[k].lz){
t[lc].mx+=t[k].lz;
t[rc].mx+=t[k].lz;
t[lc].lz+=t[k].lz;
t[rc].lz+=t[k].lz;
t[k].lz=0;
}
}
void build(int k,int l,int r,int cas){
t[k].l=l,t[k].r=r;
if(l==r){
if(cas==1) t[k].mx=a[l];
else t[k].mx=b[l];
t[k].pos=l;//主要维护一个最大值的位置
t[k].lz=0;
return;
}
int mid=(l+r)>>1;
build(lc,l,mid,cas);
build(rc,mid+1,r,cas);
pushup(k);
}
void update(int k,int l,int r,int val){
if(t[k].l>=l && t[k].r<=r){
t[k].lz+=val;
t[k].mx+=val;
return;
}
pushdown(k);
int mid=(t[k].l+t[k].r)>>1;
if(l<=mid) update(lc,l,r,val);
if(r>mid) update(rc,l,r,val);
pushup(k);
}
node query(int k,int l,int r){
if(t[k].l>=l && t[k].r<=r) return (node){t[k].mx,t[k].pos};
pushdown(k);
int mid=(t[k].l+t[k].r)>>1;
node res,now;res.mx=-inf;
if(l<=mid){
now=query(lc,l,r);
if(res.mx<now.mx){
res.mx=now.mx;
res.pos=now.pos;
}
}
if(r>mid){
now=query(rc,l,r);
if(res.mx<now.mx){
res.mx=now.mx;
res.pos=now.pos;
}
}
return res;
}
}A,B;
int solve(int l,int r){
if(l>r) return -inf;
node ma=A.query(1,l,r);//A的最大值
node mb=B.query(1,l,r);//B的最大值
if(ma.pos<=mb.pos) return min(ma.mx,mb.mx);//当Amax 的位置在Bmax的位置后面的情况,返回最小值
else{
//if(ma.mx<=mb.mx) return max(A.query(1,l,mb.pos).mx,solve(mb.pos+1,r));
//else return max(B.query(1,ma.pos,r).mx,solve(l,ma.pos-1));
int mn=min(ma.mx,mb.mx);
int ss=max(solve(mb.pos+1,ma.pos-1),max(A.query(1,l,mb.pos).mx,B.query(1,ma.pos,r).mx));//递归中间部分,和两边的最最大值相比较
return min(mn,ss);//和真个区间的最大值向比较
}
}
int main(){
//freopen("girl.in","r",stdin);
//freopen("girl.out","w",stdout);
get(n);get(m);
for(int i=1;i<=n;i++) get(a[i]);
for(int i=1;i<=n;i++) get(b[i]);
A.build(1,1,n,1);B.build(1,1,n,2);
char str;int l,r,k;
for(int i=1;i<=m;i++){
cin>>str;
get(l);get(r);get(k);
if(str=='A') A.update(1,l,r,k);
else B.update(1,l,r,k);
get(l);get(r);
cout<<solve(l,r)<<endl;
}
return 0;
}
算法三:std算法,先预处理该区间每一个小区间的最大值,再二分。
期望得分:100分;
代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
namespace IO{
char buf[1000010],*cur=buf+1000010;
inline char getc(){
(cur==buf+1000010)?fread(cur=buf,1,1000010,stdin):0;
return *cur++;
}
char buff[1000010],*curr=buff;
inline void flush(){
fwrite(buff,1,curr-buff,stdout);
}
inline void putc(const char &ch){
(curr==buff+1000010)?fwrite(curr=buff,1,1000010,stdout):0;
*curr++=ch;
}
inline void rd(int &x){
x=0;char ch=getc();int f=1;
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getc();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getc();
}
x*=f;
}
char st[60];int tp;
void PT(int x){
if(x==0)putc('0');
else{
if(x<0){
putc('-');
x=-x;
}
while(x>0){
st[++tp]=x%10+'0';
x/=10;
}
}
while(tp)putc(st[tp--]);
}
}
using IO::getc;
using IO::putc;
using IO::rd;
using IO::PT;
int n,q;
#define Maxn 1000010
int max1[Maxn<<2],max2[Maxn<<2],tag1[Maxn<<2],tag2[Maxn<<2];
int A[Maxn],B[Maxn];
void pushup(int k){
max1[k]=max(max1[k<<1],max1[k<<1|1]);
max2[k]=max(max2[k<<1],max2[k<<1|1]);
}
void make_tag(int k,int x,int y){
max1[k]+=x;
max2[k]+=y;
tag1[k]+=x;
tag2[k]+=y;
}
void pushdown(int k){
make_tag(k<<1,tag1[k],tag2[k]);
make_tag(k<<1|1,tag1[k],tag2[k]);
tag1[k]=0;
tag2[k]=0;
}
void build(int k,int l,int r){
if(l==r){
max1[k]=A[l];
max2[k]=B[l];
return ;
}
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
return ;
}
void update(int k,int l,int r,int ql,int qr,int val,bool ty){
if(l>=ql&&r<=qr){
if(ty)make_tag(k,val,0);
else make_tag(k,0,val);
return ;
}
pushdown(k);int mid=l+r>>1;
if(qr<=mid) update(k<<1,l,mid,ql,qr,val,ty);
else if(mid<ql) update(k<<1|1,mid+1,r,ql,qr,val,ty);
else update(k<<1,l,mid,ql,mid,val,ty),update(k<<1|1,mid+1,r,mid+1,qr,val,ty);
pushup(k);
return ;
}
pair<int,int> ans[Maxn<<2];
void query(int k,int l,int r,int ql,int qr){//不同寻常的query函数 ,用来预处理每一个小区间的最大值
if(l>=ql&&r<=qr){
ans[k]=make_pair(max1[k],max2[k]);//找到当前区间
return ;
}
pushdown(k);
int mid=l+r>>1;
if(qr<=mid){
query(k<<1,l,mid,ql,qr);
ans[k]=ans[k<<1];
}else if(mid<ql){
query(k<<1|1,mid+1,r,ql,qr);
ans[k]=ans[k<<1|1];
}else{
query(k<<1,l,mid,ql,qr);
query(k<<1|1,mid+1,r,ql,qr);
ans[k].first=max(ans[k<<1].first,ans[k<<1|1].first);
ans[k].second=max(ans[k<<1].second,ans[k<<1|1].second);
}
}
int calc(int k,int l,int r,int ql,int qr){//线段树上二分 ,反回该区间上的一个分节点前后最值的最小值,分治思想
if(l>=ql&&r<=qr){//当前区间被包围
if(l==r){
return min(max1[k],max2[k]);//找到点
}
int mid=l+r>>1;
pushdown(k);
int s1=max1[k<<1];
int s2=max2[k<<1|1];
if(s1>s2){//A的最值在B的最值前面,s2一定会过但是可选择过不过s1
return min(s1,max(s2,calc(k<<1,l,mid,l,mid+1)));
}else{
return min(s2,max(s1,calc(k<<1|1,mid+1,r,mid+1,r)));//A的最值在B的最值后面。
}
}
int mid=l+r>>1;
if(qr<=mid){
return calc(k<<1,l,mid,ql,qr);
}else if(mid<ql){
return calc(k<<1|1,mid+1,r,ql,qr);
}else{
int s1=ans[k<<1].first;
int s2=ans[k<<1|1].second;
if(s1>s2){
return min(s1,max(s2,calc(k<<1,l,mid,ql,mid)));
}else{
return min(s2,max(s1,calc(k<<1|1,mid+1,r,mid+1,qr)));
}
}
}
inline void print(int x){
if(x<0){
x=~x+1;
putchar('-');
}
if(x>9) print(x/10);
putchar(x%10+'0');
}
char opt[10];
int main()
{
cin>>n>>q;
for(int i=1;i<=n;i++){
cin>>A[i];
}
for(int i=1;i<=n;i++){
cin>>B[i];
}
build(1,1,n);
int l,r,d;
for(int i=1;i<=q;i++){
char ch=getchar();
while(ch!='A'&&ch!='B'){
ch=getchar();
}
cin>>l>>r>>d;
if(ch=='A'){
update(1,1,n,l,r,d,1);
}else{
update(1,1,n,l,r,d,0);
}
cin>>l>>r;
query(1,1,n,l,r);
print(calc(1,1,n,l,r));
putchar('\n');
}
return 0;
}
后面的题都不会了,我太蒟蒻了.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】