0715练习赛
A 烧饼管够
问题描述
何老板来到一家自助餐馆,这里的烧饼可以随便吃。
烧饼叠成A,B两摞,烧饼的大小不同,吃掉它所花时间也有不同。
A摞有个烧饼,从上到下吃掉第个需耗时分钟;
B摞有个烧饼,从上到下吃掉第个需耗时分钟;
两摞可以任意取,不过只能按从上往下的顺序依次取饼来吃。
何老板还有分钟的时间,他想知道,他最多能吃掉多少个烧饼?
输入格式
第一行,一个整数
第二行,
第三行,一个整数
接下来行,每行两个整数,表示一道题目 。
输出格式
行,每行一个整数,表示一道题目的答案
样例输入1
3 4 240
60 90 120
80 150 80 150
样例输出1
3
样例输入2
3 4 730
60 90 120
80 150 80 150
样例输出2
7
一眼看过去好像有点像贪心,但仔细一想,如果按照每摞最小的取,则这组数据会有问题
6 6 105
100 1 1 1 1 1
99 99 99 99 99 99
但发现每次只能从上往下依次取饼来吃
所以每摞上取的饼一定是该段序列的前缀
考虑维护的前缀和,
暴力枚举,在上二分查询或找其最大的符合条件的
耗时
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX_N = 200000 + 5;
int n,m,ans;
ll a[MAX_N],b[MAX_N],k,sa[MAX_N],sb[MAX_N];
int main(){
scanf("%d%d%lld",&n,&m,&k);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
sa[i]=sa[i-1]+a[i];
}
for(int i=1;i<=m;i++){
scanf("%lld",&b[i]);
sb[i]=sb[i-1]+b[i];
}
int p=m;
for(int i=0;i<=n;i++){
if(sa[i]>k) break;
while(sb[p]>k-sa[i] && p>=0) p--;
ans=max(ans,i+p);
}
printf("%d",ans);
return 0;
}
B 加法练习
问题描述
何老板教小朋友做加法。
何老板给出一个正整数数列 ,其中任意元素都满足
何老板给小朋友们布置了道加法题:
每道题给出两个整数,小朋友可以重复执行加法操作,每一次操作,将的值改为,问最少几次操作,就能使,
每道题目都需要小朋友快速给出答案。
输入格式
第一行,一个整数
第二行,
第三行,一个整数
接下来行,每行两个整数,表示一道题目。
输出格式
行,每行一个整数,表示一道题目的答案
样例输入 1
3
1 1 1
3
1 1
2 1
3 1
样例输出 1
2
1
1
样例输入 2
10
3 5 4 3 7 10 6 7 2 3
10
4 5
2 10
4 6
9 9
9 2
5 1
6 4
1 1
5 6
6 4
样例输出2
1
1
1
1
1
1
1
2
1
1
考虑根号分治,对进行分治
对于,进行预处理
设表示使的操作数
对于,很大,每次操作加的就很多,直接暴力模拟,最多加次,所以
耗时
#include<bits/stdc++.h>
using namespace std;
const int MAX_N = 100000 + 5;
const int MAX_S = 350 + 5;
int n,m,a[MAX_N],p,k,f[MAX_N][MAX_S];
int main(){
// freopen("badd.in","r",stdin);
// freopen("badd.out","w",stdout);
scanf("%d",&n);
int sqrtn=sqrt(n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=n;i>=1;i--){
for(int j=sqrtn;j>=1;j--){
if(i+a[i]+j>n) f[i][j]=1;
else f[i][j]=f[i+a[i]+j][j]+1;
}
}
scanf("%d",&m);
while(m--){
scanf("%d%d",&p,&k);
int ans=0;
if(k<=sqrtn){
printf("%d\n",f[p][k]);
}
else{
while(p<=n){
ans++;
p=p+a[p]+k;
}
printf("%d\n",ans);
}
}
return 0;
}
C 公约数和
问题描述
何老板用内的整数构成一个长度为的整数数列。
他发现可以构造出种不同的数列。
对于其中第种数列,其公约数
何老板想请你帮忙计算出每个数列的公约数,并求出这些公约数之和。也就是计算
答案可能很大再输出
输入格式
第一行,两个整数
输出格式
一个整数,表示计算结果
样例输入 1
3 2
样例输出 1
9
样例输入 2
3 200
样例输出 2
10813692
样例输入 3
100000 100000
样例输出 3
742202979
因为有种不同数列,算出每个数列公约数不现实,考虑算出每个数列对答案的贡献。
又因为,我们考虑内每个数字对答案的贡献。对于数字,在内,只有共个。
以为公约数的数字有个,构成的数列有种方案。
记录以为最大公约数的方案数,
但会把公约数大于(的倍数)的方案也算进去,所以必须把的倍数为公约数的方案从中减掉
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const int MAX_N = 100000 + 5;
int n,k;
ll ksm(ll x,ll y){
ll res=1;
while(y){
if(y&1) res=res*x%mod;
x=x*x%mod;
y>>=1;
}
return res;
}
ll ans[MAX_N],sum;
int main(){
cin>>n>>k;
for(int x=k;x>=1;x--){
ans[x]=ksm(k/x,n);
for(int y=x+x;y<=k;y+=x){
ans[x]=(ans[x]-ans[y]+mod)%mod;
}
sum=(sum+ans[x]*x)%mod;
}
cout<<sum;
return 0;
}
D 小球入盒
问题描述
何老板有一盒小球,每个小球上都有一个整数,所有小球上的数字都不相同。一不小心,盒子被打翻了,小球散落在地。
何老板开始捡起小球装入盒子。他会执行次操作,操作分下面两种:
操作A捡球:捡起一个小球放入盒子,该小球的数字为(表示为);
操作B提问:给出一个数字,问当前盒中的小球,哪一个球上的数字除以的余数最小?输出最小余数 (表示为)对于每个提问,何老板要求你快速给出回答。 ]
数据保证,第一个操作一定是捡球
输入格式
第一行,一个整数,表示操作的总次数
接下来行,每行描述一个操作。每个操作由一个字母和一个数字表示,字母‘A’表示操作A,字母‘B’表示操作B
输出格式
对于每个提问操作,输出一行一个整数 ,表示答案
样例输入
5
A 3
A 5
B 6
A 9
B 4
样例输出
3
1
根号分块。
设的最大值为,
以为阈值,对分块
情况1,
暴力求解:数组,表示当前集合中的最小值,每次询问,修改
情况2,
枚举倍数:要是的值尽量小,可以枚举的倍数,然后查询数列中大于等于的最小一个数字即可,因为,所以最多枚举个
一次查询
#include<bits/stdc++.h>
using namespace std;
const int MAX_N = 100000 + 5;
const int MAX_X = 300000;
int n,m,g[600 + 5];
set<int> S;
int main(){
m=sqrt(MAX_X);
scanf("%d",&n);
memset(g,0x3f,sizeof(g));
S.insert(MAX_X+1);
for(int i=1;i<=n;i++){
char op=getchar();
while(op!='A' && op!='B') op=getchar();
int x;
scanf("%d",&x);
if(op=='A'){
S.insert(x);
for(int i=1;i<=m;i++) g[i]=min(g[i],x%i);
}
else{
int ans;
if(x<=m) printf("%d\n",g[x]);
else{
ans=1e9;
int tmp;
for(int k=0;k<=MAX_X;k+=x){
tmp=*S.lower_bound(k);
if(tmp<=MAX_X) ans=min(ans,tmp%x);
}
printf("%d\n",ans);
}
}
}
return 0;
}
E 开奶茶店
问题描述
何老板到某岛国旅行。该国由编号到的座岛构成,座桥将所有岛连接了起来,任意两岛都可相互到达,每座桥的长度都是1公里。
何老板特别喜欢喝奶茶,可是他发现,只有1号岛有奶茶店,这让其他岛的人们很不方便喝到奶茶。于是他决定投资,开一些奶茶店,接下来有个事件会发生,事件有两种类型:
事件1,开店:格式,表示何老板在号岛开了一家奶茶店;
事件2,询问:格式,表示何老板想知道,与号岛距离最近的一家奶茶店的距离是多少?对于每个询问,请你快速做出回答。
输入格式:
第一行,两个整数
接下来行,每行两个整数,表示一座桥两端的岛的编号
接下来行,每行两个整数,表示一个事件
输出格式:
若干行,每行一个整数,依次对应一次询问的答案
样例输入
5 4
1 2
2 3
2 4
4 5
2 1
2 5
1 2
2 5
样例输出
0
3
2
根号分块。。。。
因为有询问和修改两个操作,而对答案造成影响的只有修改,所以
按修改操作次数分块,每隔次修改操作,进行一次,更新每个点到奶茶店的距离。
修改操作:
多起点计算新开的个奶茶店到每个岛的距离,更新数组
记录点岛最近奶茶店的距离。
每次耗时,最多进行次,共耗时)
查询操作:一次查询耗时
对于第次询问,有可能内存储的答案并不最优,因为它可能发生在某次修改操作的中间,还没来得及用更新数组,所以直接暴力计算这次修改操作的点与点的距离。
#include<bits/stdc++.h>
using namespace std;
const int MAX_N = 100000 + 5;
int n,m,sqrtn;
int Last[MAX_N],Next[MAX_N<<1],End[MAX_N<<1],tot;
inline void addedge(int x,int y){
End[++tot]=y,Next[tot]=Last[x],Last[x]=tot;
}
int dep[MAX_N],fa[MAX_N][20],dis[MAX_N],lg[MAX_N];
void dfs(int x){
dep[x]=dep[fa[x][0]]+1;
dis[x]=dep[x]-1;
for(int i=1;i<=lg[dep[x]];i++) fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=Last[x];i;i=Next[i]){
int y=End[i];
if(y!=fa[x][0]){
fa[y][0]=x;
dfs(y);
}
}
}
int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
while(dep[x]>dep[y]) x=fa[x][lg[dep[x]-dep[y]]-1];
if(x==y) return x;
for(int i=lg[dep[x]]-1;i>=0;i--){
if(fa[x][i]!=fa[y][i]){
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];
}
queue<int> q;
bool vis[MAX_N];
int p[MAX_N],cnt;
bool mark[MAX_N];
void bfs(){
memset(mark,0,sizeof(mark));
while(q.size()){
int x=q.front();
q.pop();
if(mark[x]) continue;
mark[x]=1;
for(int i=Last[x];i;i=Next[i]){
int y=End[i];
if(!mark[y]){
q.push(y);
dis[y]=min(dis[y],dis[x]+1);
}
}
}
}
inline void modify(int x){
if(vis[x]) return;
vis[x]=1;
dis[x]=0;
p[++cnt]=x;
q.push(x);
if(cnt==sqrtn){
bfs();
cnt=0;
}
}
int main(){
memset(dis,0x3f,sizeof(dis));
scanf("%d%d",&n,&m);
sqrtn=sqrt(n);
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y);
addedge(y,x);
}
for(int i=1;i<=n;i++) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
dfs(1);
vis[1]=1;
while(m--){
int op,x;
scanf("%d%d",&op,&x);
if(op==1){
modify(x);
}
else{
int ans=dis[x];
for(int i=1;i<=cnt;i++){
int y=p[i];
ans=min(ans,dep[x]+dep[y]-2*dep[lca(x,y)]);
}
printf("%d\n",ans);
}
}
return 0;
}
总结:
今天道题,有道根号分块,可以说是根号分块专项练习题了,但我居然只看出了一道,而且对分块的目标也选择错了。
题都推了一半了,但到最后竟然没有考虑进去重复的情况。
根号分块目的是对于一部分数据采用一种方法解决,对于另一部分采用另一种,而来区分这两部分的信息一定是较大的,同时对于这信息一定是题目中的关键信息,比如今天的题,有询问和修改两个操作,对于整道题,只有修改操作会对答案造成影响,所以对修改操作的次数进行分块。
总的来说,还是题做少了,经验不够
这套题应该达到的分数:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现