2023.8.16 普及模拟1

众所周知,要有一张头图。

某些注意事项

image

这次比赛呢,就很无语……

total=50pts+80pts+80pts+"0"pts

题目感觉除了T1都好做,其它的题分数都错的很冤。就当教训吧。

T1 Past

题面

给出 n,d ,和序列 a.

  • d=1 时,求所有的子区间和。
  • d=2 时,求所有子区间中最大的极差。
  • d=3 时,求所有子区间的平均数之和。

思路

部分分

前一半分还是好做的(考场也只得了这些分……)

只要暴力 O(n3) 的算法就行,当然如果使用 ST算法 或 其他优化算法 可得 60 分或更高。

100pts

image

还有一些需要注意的细节,如要把负数转正,模数时要注意

赛时代码

点击查看代码
#include<bits/stdc++.h> #define int long long using namespace std; const int N=3e6+10,mod=1336363663; int maxx,minn,n,d,a[N],sum[N],b[N],ans,jc,tot=1; double pj; inline int read(){ int x=0;bool f=0;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=1;s=getchar();} while(s>='0'&&s<='9'){x=(x<<1)+(x<<3)+(s^48);s=getchar();} return f?-x:x; } signed main(void){ freopen("pst.in","r",stdin); freopen("pst.out","w",stdout); n=read();d=read(); for(int i=1;i<=n;++i){ a[i]=read(); sum[i]=(sum[i-1]+a[i])%mod; } if(!d) return 0; if(d==1){ for(int i=1;i<=n;++i){ for(int l=1;l+i-1<=n;++l){ int r=l+i-1; ans=(ans+sum[r]-sum[l-1])%mod; } } printf("%lld",ans); } if(d==2){ for(int i=1;i<=n;++i){ for(int l=1;l+i-1<=n;++l){ int r=l+i-1; maxx=-0x3f3f3f3f,minn=0x3f3f3f3f; ans=(ans+sum[r]-sum[l-1])%mod; for(int k=l;k<=r;++k){ maxx=maxx>a[k]?maxx:a[k]; minn=minn<a[k]?minn:a[k]; } jc=(jc+maxx-minn)%mod; } } printf("%lld\n%lld",ans,jc); } if(d==3){ for(int i=1;i<=n;++i){ tot=tot*i%mod; for(int l=1;l+i-1<=n;++l){ int r=l+i-1; maxx=-0x3f3f3f3f,minn=0x3f3f3f3f; ans=(ans+sum[r]-sum[l-1])%mod; for(int k=l;k<=r;++k){ maxx=maxx>a[k]?maxx:a[k]; minn=minn<a[k]?minn:a[k]; } jc=(jc+maxx-minn)%mod; pj=pj+(double)(1.0*(sum[r]-sum[l-1])/i); } } printf("%lld\n%lld\n",ans,jc); cout<<(int)(pj*1.0*tot)%mod; } return 0; /* 3 3 3 2 5 */ }

赛后代码

点击查看代码
#include<bits/stdc++.h> #define int long long using namespace std; const int N=3e6+10,mod=1336363663; int maxx,minn,n,d,a[N],sum[N],b[N],ans,jc,tot,pp; int o[N],f1[N],f2[N]; double pj;stack<int>q,p; inline int read(){ int x=0;bool f=0;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=1;s=getchar();} while(s>='0'&&s<='9'){x=(x<<1)+(x<<3)+(s^48);s=getchar();} return f?-x:x; } signed main(void){ freopen("pst.in","r",stdin); freopen("pst.out","w",stdout); n=read();d=read(); f1[0]=f2[n+1]=1; for(int i=1;i<=n;++i){ a[i]=read(); sum[i]=(sum[i-1]+a[i])%mod; f1[i]=f1[i-1]*i%mod; } for(int i=n;i;--i){ f2[i]=f2[i+1]*i%mod; } if(!d) return 0; if(d==1){ for(int i=1;i<=n;++i) ans=(ans+a[i]*i%mod*(n-i+1))%mod; //它前面的和后面的 printf("%lld",ans); } if(d==2){ for(int i=1;i<=n;++i) ans=(ans+a[i]*i%mod*(n-i+1)%mod)%mod; a[n+1]=LLONG_MAX; for(int x,i=1;i<=n+1;++i){ while(!q.empty()&&a[q.top()]<=a[i]){ x=q.top();q.pop(); jc=(jc+a[x]*((i-o[x]-1)%mod+(int)(x-o[x]-1)*(i-x-1)%mod+mod)%mod)%mod; } if(q.empty()) o[i]=0; else o[i]=q.top(); q.push(i); } stack<int>().swap(q); a[n+1]=-114154; for(int x,i=1;i<=n+1;++i){ while(!q.empty()&&a[q.top()]>=a[i]){ x=q.top();q.pop(); jc=(jc-a[x]*((i-o[x]-1)%mod+1ll*(x-o[x]-1)*(i-x-1)%mod+mod)%mod+mod)%mod; } if(q.empty()) o[i]=0; else o[i]=q.top(); q.push(i); } printf("%lld\n%lld",ans,jc); } if(d==3){ for(int i=1;i<=n;++i) ans=(ans+a[i]*i%mod*(n-i+1)%mod)%mod; a[n+1]=LLONG_MAX; for(int x,i=1;i<=n+1;++i){ while(!q.empty()&&a[q.top()]<=a[i]){ x=q.top();q.pop(); jc=(jc+a[x]*((i-o[x]-1)%mod+(int)(x-o[x]-1)*(i-x-1)%mod+mod)%mod)%mod; } if(q.empty()) o[i]=0; else o[i]=q.top(); q.push(i); } stack<int>().swap(q); a[n+1]=-114154; for(int x,i=1;i<=n+1;++i){ while(!q.empty()&&a[q.top()]>=a[i]){ x=q.top();q.pop(); jc=(jc-a[x]*((i-o[x]-1)%mod+1ll*(x-o[x]-1)*(i-x-1)%mod+mod)%mod+mod)%mod; } if(q.empty()) o[i]=0; else o[i]=q.top(); q.push(i); } for(int i=0;i<n;++i){ pp=(pp+sum[n-i]-sum[i]+mod)%mod; tot=(tot+pp*f1[i]%mod*f2[i+2])%mod; } printf("%lld\n%lld\n%lld",ans,jc,tot); } return 0; /* 3 3 3 2 5 */ }

T2 Present

题面

模拟双端队列题,5 种操作。

  • 往前出入一个数
  • 查询前面第一个数
  • 往后出入一个数
  • 查询后面第一个数
  • 翻转数列

思路

水题,唯一需要注意的是不要暴力翻转数列,可以拿一个变量记录是否反转,在做。(没想到这一点,悲)

赛时代码

点击查看代码
#include<bits/stdc++.h> using namespace std; const int N=1e5+10; deque<int>q; int n,id,a[N]; signed main(void){ freopen("prs.in","r",stdin); freopen("prs.out","w",stdout); scanf("%d%d",&n,&id); for(int opt,x,o,i=1;i<=n;++i){ scanf("%d",&opt); if(opt==0){ scanf("%d",&x); q.push_front(x); } if(opt==1){ if(!q.size()){ cout<<0<<endl; } else{ cout<<q.front()<<endl; q.pop_front(); } } if(opt==2){ scanf("%d",&x); q.push_back(x); } if(opt==3){ if(!q.size()){ cout<<0<<endl; } else{ cout<<q.back()<<endl; q.pop_back(); } } if(opt==4){ o=q.size(); for(int i=1;i<=o;++i){ a[i]=q.front(); q.pop_front(); } for(int i=o;i;--i){ q.push_back(a[i]); a[i]=0; } } } return 0; /* 8 0 1 2 111 0 222 4 0 333 3 1 3 */ }

赛后代码

点击查看代码
#include<bits/stdc++.h> using namespace std; const int N=1e5+10; deque<int>q;bool pd; int n,id,a[N]; signed main(void){ freopen("prs.in","r",stdin); freopen("prs.out","w",stdout); scanf("%d%d",&n,&id); for(int opt,x,o,i=1;i<=n;++i){ scanf("%d",&opt); if(opt==0){ scanf("%d",&x); if(!pd) q.push_front(x); else q.push_back(x); } if(opt==1){ if(!q.size()) puts("0"); else{ if(!pd){ cout<<q.front()<<endl; q.pop_front(); } else{ cout<<q.back()<<endl; q.pop_back(); } } } if(opt==2){ scanf("%d",&x); if(!pd) q.push_back(x); if(pd) q.push_front(x); } if(opt==3){ if(!q.size()) puts("0"); else{ if(!pd){ cout<<q.back()<<endl; q.pop_back(); } else{ cout<<q.front()<<endl; q.pop_front(); } } } if(opt==4){ if(pd) pd=0; else pd=1; } } return 0; /* 8 0 1 2 111 0 222 4 0 333 3 1 3 */ }

T3 Future

题面

一棵树,问从根节点(编号为 1 的节点)到叶子结点的最大距离(叶子结点任意,不过要是最长距离),边都是有向的。

思路

法一:SPFA 跑最长路

只需注意两点,把点权变成边权,初始值负无穷(没注意到为 90 分)。赛时刚开始是这么写的,但又想这毕竟是死掉的算法,就换成了树形dp。

code

点击查看代码
#include<bits/stdc++.h> #define int long long using namespace std; const int N=3e5+10; int ans=-0x3f3f3f3f,n,h[N],cnt,s[N],d[N],a[N]; struct node{int to,nxt,w;}e[N]; inline void add(int x,int y,int z){ e[++cnt].to=y;e[cnt].nxt=h[x];e[cnt].w=z; h[x]=cnt; } queue<int>q;bool vis[N]; inline void spfa(){ memset(a,0x3f,sizeof a); memset(vis,0,sizeof vis); q.push(1);a[1]=s[1];vis[1]=1; while(!q.empty()){ int u=q.front();q.pop();vis[u]=false; for(int i=h[u];i;i=e[i].nxt){ int v=e[i].to; if(a[v]>a[u]+e[i].w){ a[v]=a[u]+e[i].w; if(!vis[v]){ q.push(v); vis[v]=1; } } } } return ; } signed main(void){ freopen("ftr.in","r",stdin); freopen("ftr.out","w",stdout); scanf("%lld",&n); for(int i=1;i<=n;++i) scanf("%lld",s+i); for(int x,i=1;i<=n;++i){ scanf("%lld",&x); add(x,i,s[i]); ++d[x]; } spfa(); for(int i=1;i<=n;++i){ if(!d[i]){ ans=max(ans,a[i]); } } printf("%lld\n",ans); /* 5 1 2 3 4 5 0 1 2 3 2 */ }

法二:树形dp

板子,不多说,注意初始值,和判叶子结点。

code

点击查看代码
#include<bits/stdc++.h> #define int long long using namespace std; const int N=3e5+10; int ans,n,h[N],cnt,s[N],d[N],a[N],dp[N]; struct node{int to,nxt,w;}e[N]; inline void add(int x,int y,int z){ e[++cnt].to=y;e[cnt].nxt=h[x];e[cnt].w=z; h[x]=cnt; } // queue<int>q;bool vis[N]; // inline void spfa(){ // memset(a,0x3f,sizeof a); // memset(vis,0,sizeof vis); // q.push(1);a[1]=s[1];vis[1]=1; // while(!q.empty()){ // int u=q.front();q.pop();vis[u]=false; // for(int i=h[u];i;i=e[i].nxt){ // int v=e[i].to; // if(a[v]>a[u]+e[i].w){ // a[v]=a[u]+e[i].w; // if(!vis[v]){ // q.push(v); // vis[v]=1; // } // } // } // } // return ; // } inline void dfs(int x,int fa){ if(!h[x]) dp[x]=0; for(int i=h[x];i;i=e[i].nxt){ int v=e[i].to; dfs(v,x); dp[x]=max(dp[x],dp[v]); } dp[x]=dp[x]+s[x]; // cout<<x<<" "<<dp[x]<<endl; } signed main(void){ freopen("ftr.in","r",stdin); freopen("ftr.out","w",stdout); memset(dp,-0x3f,sizeof dp); scanf("%lld",&n); for(int i=1;i<=n;++i) scanf("%lld",s+i); for(int x,i=1;i<=n;++i){ scanf("%lld",&x); add(x,i,s[i]); ++d[x]; } // spfa(); // for(int i=1;i<=n;++i){ // if(!d[i]){ // ans=max(ans,a[i]); // } // } dfs(1,0); printf("%lld\n",dp[1]); /* 5 1 2 3 4 5 0 1 2 3 2 */ }

法3:拓扑优化dp,弱化版缩点

奇奇怪怪的做法捏……

T4 Beyond

题面太长,就不放了。

思路

meet in middle ,分别从 (1,1) (往 对角线上的点 搜索) 和 对角线上的点(往 (n,n) 搜索) 进行爆搜,当到达当下区域时,将所得到的值分别用两个 map 进行存储,又因为在当下区域可以进行传送至另一个当下区域,然后利用加法原理和乘法原理求解。
注意:当遍历 map 时用了range-based for + auto,请注意使用 C++ 14 -O2 ,不然会 CE(血的教训)。

(range-based for+auto)+(C++ -O2)+(AC 代码) = "0" 分

赛时( AC )代码

点击查看代码
#include<bits/stdc++.h> #define int long long using namespace std; const int N=100; const int dx[]={1,0}; const int dy[]={0,1}; int ans,n,F,a[N][N],dp[N][N]; map<int,int>p,q; bool vis[N][N]; inline int read(){ int x=0;bool f=0;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=1;s=getchar();} while(s>='0'&&s<='9'){x=(x<<1)+(x<<3)+(s^48);s=getchar();} return f?-x:x; } inline void dss(int x,int y,int cnt){ if(x+y-1==n){ ++q[cnt]; return ; } for(int i=0;i<2;++i){ int xx=x+dx[i],yy=y+dy[i]; if(xx<=n&&y<=n&&x+y-1<=n) dss(xx,yy,cnt^a[xx][yy]); } } inline void dff(int x,int y,int cnt){ if(x==n&&y==n){ ++p[cnt]; return ; } for(int i=0;i<2;++i){ int xx=x+dx[i],yy=y+dy[i]; if(xx<=n&&y<=n) dff(xx,yy,cnt^a[xx][yy]); } } signed main(void){ freopen("byd.in","r",stdin); freopen("byd.out","w",stdout); n=read();F=read(); for(int i=1;i<=n;++i){ for(int j=1;j<=n;++j){ a[i][j]=read(); } } // if(n==1){ // if(a[1][1]+F==a[1][1]) ++ans; // printf("%lld\n",ans); // } // else if(n==2){ // int x=a[1][1]^a[1][2],y=a[1][1]^a[2][1]; // int z=x^a[2][2],g=y^a[2][2]; // if(x+F==z) ans+=2; // if(y+F==g) ans+=2; // printf("%lld\n",ans); // } // else{ dss(1,1,0); for(int i=1;i<=n;++i) dff(i,n-i+1,0); for(auto i:p){ int o=i.first-F; if(q.find(o)!=q.end()) ans+=i.second*q[o]; } printf("%lld\n",ans); // } return 0; }

总结

选好编译环境,看清呐,我还以为学校OJ的 C++ -O2 默认 C++14 呢,还要注意文件输入输出。
image
image


__EOF__

本文作者GOD_HJ
本文链接https://www.cnblogs.com/GOD-HJ/p/17633895.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   GOD_HJ  阅读(59)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
点击右上角即可分享
微信分享提示